Emojery API
An API over the aggregate reaction counts, for dashboards, research, and widgets.
Not available yet. Endpoints below are a design preview rather than a contract. They will roll out gradually starting with the next few releases. Follow the repo or join the waitlist below to be the first to know when an endpoint goes live.
Get notified at launch
Leave your email and we will send a single message when the first endpoint goes live. No newsletter, no follow-ups — one launch email, then the address has done its job.
We store the address you enter (plus a hashed copy for de-duplication) until launch, only to send that one email. Details in the privacy policy.
Principles
- Open dataset. Reaction counts are facts about public web pages. The data itself is released under a permissive license (see below) — once you have a response, you can republish, mirror, and build on it freely. What is metered is the serving; the facts stay free.
- Paid access. Aggregating and serving fresh reaction counts across millions of pages takes significant compute. To keep the project alive without ads or data resale, API access will be paid. A free tier for small projects and open research is planned; full pricing will be published before the first endpoint goes live.
- No personal data, ever. The API will only ever expose aggregates. There is no "who reacted" endpoint and there never will be. The per-account reaction records that let you change or remove a reaction never feed an API response, and neither does the context a reaction carries under the extension's "Community insights" setting: city, language, browser and operating system stay inside the service. The one derivative under consideration is a per-country map of reactions, published only above a minimum count per cell and never broken down per target; if it ships, the threshold ships with it.
- Nothing the log does not already say. Every count the API serves is recomputable from the public transparency log with the verifier. What a paid plan buys is freshness, throughput, ready-made rankings and uptime, never access to a fact that is otherwise withheld. A number you can already derive for free stays free to derive.
- Cache-friendly. Every response will set
Cache-ControlandETagheaders so the edge does the heavy lifting and your client doesn't pay for stale requests. CORS will be open to*.
Planned endpoints
1. Per-target reactions
Get the full reaction breakdown for one public target — a Facebook post, a GitHub repo, an Amazon product, and whatever else is supported by the time the endpoint ships.
GET /v1/reactions/{site}/{targetId}
{
"site": "github",
"targetId": "khasky/emojery",
"total": 1284,
"counts": {
"❤️": 612,
"🔥": 301,
"👍": 189,
"🎉": 102,
"🤔": 80
},
"lastUpdated": 1747560000000
} 2. Batch reads
The same shape, multiplexed. Useful for embedding counts on a listing page where you have many targets in one render.
GET /v1/reactions/batch?t=github/khasky/emojery&t=amazon/B08N5WRWNW 3. Top pages
The most-reacted-to targets across a site, or globally, optionally windowed to a recent period. Useful for "what's everyone talking about right now" boards.
GET /v1/top?site=github&window=24h&limit=50
{
"window": "24h",
"items": [
{ "site": "github", "targetId": "user/repo", "total": 921 },
{ "site": "github", "targetId": "user/other", "total": 743 },
...
]
}
Supported windows under consideration: 1h,
24h, 7d, 30d,
all.
4. Trending
Targets ranked by the rate of reactions in a window rather than the absolute count. A 2-day-old post that gained 500 reactions today beats a 5-year-old post that has 50,000 lifetime reactions but none today.
GET /v1/trending?site=facebook&window=1h&limit=20 5. Emoji popularity
Which emojis are most-used as reactions — globally, per site, or per time window. Answers "what does the modern web feel like today?" in one chart.
GET /v1/emojis?site=&window=7d&limit=25
{
"window": "7d",
"items": [
{ "reaction": "❤️", "count": 184_402 },
{ "reaction": "🔥", "count": 97_215 },
{ "reaction": "👍", "count": 88_117 },
...
]
} 6. Site activity
Time-bucketed totals per site: reactions per hour or per day, suitable for sparkline charts and longitudinal research. No targets, no users, just the count of votes landing in each bucket.
GET /v1/activity?site=amazon&bucket=hour&range=24h
{
"site": "amazon",
"bucket": "hour",
"range": "24h",
"buckets": [
{ "t": 1747551600000, "count": 412 },
{ "t": 1747555200000, "count": 388 },
{ "t": 1747558800000, "count": 506 },
...
]
} 7. Aggregate site stats
A one-line summary per site: total reactions ever recorded, total targets observed, number of supported items by category, and the timestamp of the most recent reaction.
GET /v1/sites/{site}/stats
{
"site": "github",
"totalReactions": 412_900,
"uniqueTargets": 18_204,
"topReactions": [ "❤️", "🔥", "👍", "🎉", "💯" ],
"lastReactionAt": 1747560000000
} 8. Creator totals
Every reaction the web left on one public account's work, rolled up by handle: a YouTube channel, a GitHub owner, a shop brand. Returns the account total, its emoji mix, and its own targets ranked, so a creator page or a profile badge is one call.
GET /v1/creators/{site}/{handle}?limit=20
{
"site": "youtube",
"handle": "@example",
"total": 48_120,
"counts": { "❤️": 21_004, "🔥": 9_880, "👍": 8_412 },
"targets": [
{ "targetId": "dQw4w9WgXcQ", "total": 6_301 },
{ "targetId": "kJQP7kiw5Fk", "total": 4_155 },
...
]
} Reactions other people left on public posts, grouped by the account that published them. It says nothing about who reacted, and the grouping comes from the public identifier of each post, never from anything about the people reacting.
9. Public mood
Emoji popularity one level up: emoji folded into named groups, so a day reads as a single mix instead of 600 separate counters. Per site, globally, and comparable between windows.
GET /v1/mood?site=&window=24h
{
"window": "24h",
"sample": 612_004,
"mix": {
"positive": 0.58,
"playful": 0.17,
"negative": 0.14,
"surprised": 0.11
},
"topEmoji": [ "❤️", "🔥", "😂" ],
"shift": { "negative": 0.04 }
} The grouping is ours and ships published alongside the endpoint. The raw per-emoji counts stay available, so you can regroup them your own way.
10. On this day
What the web reacted to on one calendar date, year by year, as far back as the log goes. Feeds a "a year ago today" strip, a retro board, or a look at how the same date felt in different years.
GET /v1/on-this-day?date=05-19&limit=10
{
"date": "05-19",
"years": [
{
"year": 2026,
"total": 88_412,
"topEmoji": "🔥",
"items": [
{ "site": "youtube", "targetId": "dQw4w9WgXcQ", "total": 6_301 },
...
]
},
{ "year": 2025, "total": 41_005, "topEmoji": "❤️", "items": [ ... ] }
]
} What you could build
6 concrete use cases the planned endpoints are designed around, from one-evening widgets to full research pipelines:
-
Sentiment dashboards
Publishers & bloggers
See how people actually feel about a video, thread, or news post on platforms that hide dislikes or collapse everything into one Like.
GET /v1/reactions/{site}/{targetId} -
Product research
Commerce
Pull reaction breakdowns for a whole listing page of products in one call and chart them next to star ratings.
GET /v1/reactions/batch -
Mood-of-the-web boards
Media & research
Rank the emojis the web is using right now, per site or globally, and watch the mix shift through the day.
GET /v1/emojis?window=24h -
Breakout detection
Brands & platforms
Catch a page that starts collecting reactions faster than its baseline, hours before lifetime totals show anything unusual.
GET /v1/trending?window=1h -
Creator widgets
Creators
Embed a live reaction strip for your repo, post, or product on your own site — the planned oEmbed endpoint makes it a one-liner.
GET /v1/reactions/github/you/your-repo -
Open research
Research
Longitudinal, PII-free reaction data for media studies and social-web research, with per-site activity series and aggregate stats. The raw per-vote log is already free to clone; the API adds the series and rankings so a study does not start with a fold.
GET /v1/activity?bucket=day
Built for
The same open aggregates read very differently depending on who's holding them. Start from your corner:
-
Creators
Read how your work really landed past a single Like, and embed a live reaction strip on your own site.
-
Publishers & bloggers
An audience-reaction signal you own — verifiable, and impossible for a platform to quietly switch off.
-
Commerce
Independent product reactions, separate from a seller's own stars, and checkable against the public log.
-
Developers
Real signal on your repos and packages, in a batchable, cache-friendly API with an open dataset.
Sample dashboards
3 small widgets showing what the responses render into. All numbers are illustrative mocks rather than live data — the shapes match the endpoint examples above.
Embeds and widgets
Beyond raw JSON, no-code surfaces so a creator, blogger, or shop can put counts on a page without touching the API:
- Reaction badge: a live SVG badge for a README, blog sidebar, or listing, showing the top 3 emoji and a running total, embedded in one line like a CI badge, rendering for everyone whether or not they have the extension.
- Profile badge: one badge for everything under an account, the reaction total across a GitHub owner's repos or a channel's videos, drawn from the same creator endpoint and made for a profile README or an about page.
- Embeddable widget: a drop-in reaction strip for your own site, carrying a "verified" mark that links back to the public proof behind the count.
- oEmbed endpoint: so platforms that already understand oEmbed (Ghost, WordPress, Substack, ...) can unfurl an Emojery target URL into an interactive reaction strip without a plug-in.
- Webhooks. Subscribe to a target and get a POST when its reaction count crosses a threshold you set. Useful for site owners who want to know when their post breaks out.
Rate limits and fair use
Every endpoint will sit behind a per-IP rate limit chosen so a normal dashboard never hits it. Heavy users (research crawlers, public dashboards with global reach) should reach out before the first launch so the budget can be tuned rather than tripped.
Responses will set
X-RateLimit-Remaining and
X-RateLimit-Reset headers, and a
429 will always include a Retry-After
value. Edge caching means most repeat fetches are free at the client.
Forks and other clients
The extension is GPL-3.0: read it, study it, modify it, and run your build locally for learning or research. What the license does not hand over is the backend: a build from the public source targets the staging environment, and pointing it at a backend of your own is a build-time setting the repository documents. Reading reactions needs no permission at all, from the public log, the status feed, or the badges.
If your client needs the hosted service instead, that is a request rather than a refusal. Write to [email protected] or open a question on the tracker with 4 things:
- what the client is, who ships it, and where its source lives;
- which endpoints it calls and roughly how often;
- how a reaction it submits stays one deliberate press by one signed-in person, which is the promise the counts rest on;
- how you will be reachable if something it does needs to stop.
What comes back is a named client with its own quota, revocable, under the same acceptable use policy everyone else signs in under. Until the data API launches this is handled case by case, and the answer to a client that would submit reactions on someone's behalf is no, for the reason the section below gives. An unapproved client calling the extension endpoints is a policy violation, and asking is simply the cheaper path: an approved client gets a quota and a conversation before anything changes under it, while an unapproved one finds out by being cut off.
Data license
Reaction counts are facts about public web pages, and the aggregates this API serves are open data. Once you hold a response you can republish it, mirror it, cache it, and build on it — commercially or not.
- Attribution welcomed, never demanded. A link back to emojery.app helps more people find the data and check it with the open-source verifier.
- No PII to license. The dataset the API serves only ever contains aggregate counts. No API response includes a user identifier of any kind, so there is nothing personal to license, leak, or de-anonymize. The public log is a different artefact: its entries carry a rotating pseudonym, which is why the log has its own terms on the privacy page.
- Terms that travel with the data. The API terms will forbid 3 uses outright, for the caller and for anyone the caller passes data to: trying to re-identify a person from counts or from the public log, using the data to assess an individual's creditworthiness or eligibility for credit, insurance, housing or employment, and reselling API output as a data product rather than building on it. These mirror the commitments on Emojery's own store listing, which apply to what is derived from user data as much as to the raw data.
- License text finalized at launch. The exact license ships together with the first endpoint, before anything goes live. The principles above are the commitment it will encode: permissive, open aggregates.
What it will not do
- No reaction submission via the public API. Votes will always go through the extension's authenticated path. A public submit endpoint would defeat the one-account-one-vote guarantee that makes the counts worth reading in the first place.
- No identity lookups. Not by provider account, not by hash, not by anything else. The public API only knows about counts.
- No private targets. If a page requires authentication to view, the extension doesn't record reactions for it and the API has nothing to return.
- No data resale. What you pay for is access to the live service — query throughput, freshness, uptime.
- No audience profiles. Not per target, not per creator, not per site: no breakdown of who reacted by city, language, device or browser, and no "your audience is 40% Berlin" panel. The extension collects that context for aggregate maps under a setting people can switch off, and turning it into a product sold about them would break both the promise made to them and the one made on the store listing. A per-country total above a minimum count is the ceiling, and it is not yet decided.
- Provenance rather than a headcount. A verifiable count proves the number wasn't rewritten after the fact and that reactions came through the extension's authenticated path. It doesn't claim each reaction is a distinct human. It's a strong, checkable engagement signal that complements bot-detection rather than replacing it.