API guide

Base URL: https://www.open-emotions.com/v1. JSON over HTTPS. The API version is in the path (/v1); changes within it are additions only.

Authentication

Send Authorization: Bearer oe_live_… on POST /v1/classify. GET /v1/meta and GET /v1/health are public.

Keys are issued by the project, one for each app that connects. A key has its own rate limit and can be revoked. To run without a key, download the weights: see Models.

Quick start

curl https://www.open-emotions.com/v1/meta

curl https://www.open-emotions.com/v1/classify \
  -H "Authorization: Bearer oe_live_…" \
  -H "Content-Type: application/json" \
  -d '{"blocks":[{"id":"a","text":"Thank you so much!"}]}'
const response = await fetch("https://www.open-emotions.com/v1/classify", {
  method: "POST",
  headers: { authorization: "Bearer oe_live_…", "content-type": "application/json" },
  body: JSON.stringify({ blocks: [{ id: "a", text: "Thank you so much!" }] }),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { results, labels, provenance } = await response.json();
console.log(results[0].scores, provenance.model);

Routes

Classify

Request

{
  "blocks": [
    { "id": "a", "text": "Thank you so much!", "before": "…", "after": "…" }
  ],
  "client": { "name": "my-app" }
}

Response

{
  "results": [
    { "id": "a", "scores": [0.02, 0.01, …] }
  ],
  "labels": "goemotions@1",
  "provenance": {
    "observer": { "id": "open-emotions", "version": "0.2.0" },
    "implementation": "…",
    "model": { "id": "…", "revision": "…" },
    "requestId": "req_…",
    "at": "2026-01-01T00:00:00.000Z",
    "durationMs": 120
  },
  "usage": { "blocks": 1, "scored": 1, "chars": 18 }
}

Reading the scores

Each score is the classifier's confidence that the emotion is expressed in the text. The labels are independent, so several can be high at once. A score is not an intensity, and not evidence of what anyone feels. See Limitations.

Errors

Every error has the same shape. retryable says whether the same request can succeed later.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests for this key. Try again in 12 seconds.",
    "retryable": true,
    "retryAfterSeconds": 12,
    "requestId": "req_…"
  }
}
StatusCodeMeaning
400invalid_requestThe body isn't JSON, or a field is missing, the wrong type or too long.
400unknown_implementationThe request named an implementation this observer doesn't offer.
401unauthorizedThe key is missing, malformed, unknown or revoked.
413payload_too_largeThe body is over 256 KB.
429rate_limitedToo many requests for this key. Wait for the Retry-After header.
502upstream_errorThe model couldn't score any block. Check retryable before trying again.
503model_loadingThe model is starting. Wait for the Retry-After header.
503unavailableThe observer isn't configured to classify yet.
500internalSomething went wrong on our side.

Limits

Browsers

The API answers cross-origin requests from any origin and never uses cookies. A key in browser code is visible to anyone, so call the API from your server.