ewgsta's cafe

Home

REST API for searching anime, browsing seasons and episodes, and streaming video.

OpenAnime API is a REST service for searching anime, browsing seasons and episodes, and streaming video.

  • Base URL: https://openanime-api.ewgsta.me
  • Format: JSON (application/json)
  • Version: 1.0.0

No authentication or API key is required for any endpoint. Stream links expire automatically (see Streaming).

Response envelope

Every response uses the same envelope, so consumers always know where to look.

Success

{
  "success": true,
  "data": { },
  "meta": {
    "timestamp": "2026-08-17T12:29:10.275Z",
    "tsc": 1786969750275,
    "request_id": "f1dc5b37-ec54-4721-86c9-74026bef40bc",
    "duration_ms": 48
  }
}

Error

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Anime not found.",
    "status": 404
  },
  "meta": {
    "timestamp": "2026-08-17T12:30:00.000Z",
    "tsc": 1786969800000,
    "request_id": "9e8da77e-6554-4145-ae14-cbec5b43c1f9",
    "duration_ms": 12
  }
}

meta fields

FieldTypeDescription
timestampstringISO-8601 UTC response time
tscnumberUnix timestamp in milliseconds
request_idstringUUIDv4, unique per request
duration_msnumberServer processing time in ms

Endpoints

MethodPathDescription
GET/healthHealth check
GET/v1/search?q=:querySearch the catalog
GET/v1/anime/:slugSeries / film details
GET/v1/anime/:slug/seasons/:season/episodesPaginated episode list
GET/v1/anime/:slug/seasons/:season/episodes/:episodeSingle episode
GET/v1/stream/:tokenStream media

GET /health

Health check. Reports the overall status and the health of the two underlying data sources under generic labels (openanime, scraper-worker).

Response — 200

{
  "success": true,
  "data": {
    "status": "operational",
    "version": "openanime-api@1.0.0",
    "upstreams": { "openanime": true, "scraper-worker": true }
  },
  "meta": { }
}
FieldTypeDescription
statusstringoperational | degraded | maintenance
versionstringAPI name and version
upstreamsobjectMap of label -> boolean reachability

GET /v1/search?q=:query

Search the catalog.

Query parameters

NameTypeRequiredDescription
qstringYesSearch query, 1–200 characters, no control characters

Response — 200

{
  "success": true,
  "data": {
    "count": 10,
    "results": [
      {
        "id": "angel-beats",
        "title": "Angel Beats!",
        "title_en": "Angel Beats!",
        "title_local": "Angel Beats!",
        "title_native": null,
        "category": "series",
        "rating": 7.8
      }
    ]
  },
  "meta": { }
}

data.count equals data.results.length.

results[] items:

FieldTypeDescription
idstringOpaque identifier — use in /v1/anime/:slug
titlestringBest-available display title
title_enstring | nullEnglish title
title_localstring | nullLocal-language title
title_nativestring | nullNative (romaji, etc.) title
categorystringseries | film | unknown
ratingnumber | nullAveraged score

Errors: missing/empty q → INVALID_QUERY (400).

GET /v1/anime/:slug

Full details for a series or film, including its season list.

Path parameters

NameTypeRequiredDescription
slugstringYesIdentifier from search. Lowercase alphanumerics + hyphens, 1–200 chars

Response — 200

{
  "success": true,
  "data": {
    "id": "angel-beats",
    "title": "Angel Beats!",
    "title_en": "Angel Beats!",
    "title_local": null,
    "title_native": "Angeru Bīts!",
    "category": "series",
    "rating": 8.1,
    "tags": ["action", "supernatural"],
    "overview": "A boy wakes up in the afterlife...",
    "seasons": [
      { "number": 1, "label": "1. Sezon", "total_episodes": 13, "has_content": true },
      { "number": 2, "label": "2. Sezon", "total_episodes": 0, "has_content": false }
    ]
  },
  "meta": { }
}
FieldTypeDescription
idstringIdentifier
title/title_en/title_local/title_nativestring | nullTitles in various languages
categorystringseries | film | unknown
ratingnumber | nullScore
tagsstring[]Genre/tag labels
overviewstring | nullSynopsis
seasonsarrayList of season summaries

seasons[] items:

FieldTypeDescription
numbernumberSeason number
labelstringHuman-readable season label
total_episodesnumberEpisode count
has_contentbooleanWhether episodes exist yet

Use number + has_content to decide which seasons can be browsed.

Errors: invalid slug → INVALID_ID (400); unknown slug → NOT_FOUND (404).

GET /v1/anime/:slug/seasons/:season/episodes

Paginated list of episodes in a season, each with its stream link.

Path parameters

NameTypeRequiredDescription
slugstringYesAnime identifier
seasonnumberYesSeason number

Query parameters (both optional, clamped automatically)

NameTypeDefaultDescription
pagenumber11–1000
per_pagenumber201–100

Response — 200

{
  "success": true,
  "data": {
    "series_id": "angel-beats",
    "season_number": 1,
    "total": 13,
    "page": 1,
    "per_page": 20,
    "items": [
      {
        "number": 1,
        "title": "Kalkış",
        "aired_at": "03.04.2010",
        "translator": "Bağımsız",
        "available_translators": ["Bağımsız", "NetRip 4K"],
        "status": "available",
        "sources": [
          { "quality": "2160p", "size_bytes": 1746991636, "codec": "mp4", "stream_url": "https://openanime-api.ewgsta.me/v1/stream/wPGsj64d..." }
        ]
      }
    ]
  },
  "meta": { }
}

items[] (EpisodeItem):

FieldTypeDescription
numbernumberEpisode number
titlestring | nullEpisode title
aired_atstring | nullAir date
translatorstring | nullActive subtitle group
available_translatorsstring[]Selectable subtitle groups
statusstringavailable | pending | unavailable
sourcesarrayQuality variants

sources[] (EpisodeSource):

FieldTypeDescription
qualitystringLabel, e.g. 1080p
size_bytesnumber | nullFile size
codecstringContainer, e.g. mp4
stream_urlstringStream link, valid 6 hours

Errors: invalid slug → INVALID_ID (400); invalid season → INVALID_SEASON (400); unavailable → NOT_FOUND (404).

GET /v1/anime/:slug/seasons/:season/episodes/:episode

Single episode with its full source list.

Path parameters

NameTypeRequiredDescription
slugstringYesAnime identifier
seasonnumberYesSeason number
episodenumberYesEpisode number

Response — 200

{
  "success": true,
  "data": {
    "series_id": "angel-beats",
    "season_number": 1,
    "episode": { }
  },
  "meta": { }
}

episode has the same shape as EpisodeItem above.

Errors: invalid slug → INVALID_ID (400); invalid season → INVALID_SEASON (400); invalid episode → INVALID_EPISODE (400); unavailable → NOT_FOUND (404).

Streaming

GET /v1/stream/:token

Opens the media file behind an episode's stream_url. Works in any player that supports HTTP range requests — seeking and resuming are handled via Range headers (responds with 206 Partial Content).

  • A link is valid for 6 hours after it is issued and can be used multiple times until it expires.
  • Forced re-expiry: request a fresh episode (or list) to get a new link whenever you need one.

Response — 200/206

Binary media stream. Example headers:

HTTP/2 206
content-type: video/mp4
content-length: 1048576
content-range: bytes 0-1048575/1746991636
accept-ranges: bytes
cache-control: private, max-age=3600

Errors:

CodeStatusDescription
TOKEN_MISSING400Link is empty
TOKEN_INVALID403Malformed link
TOKEN_EXPIRED403Link older than 6 hours
UPSTREAM_UNAVAILABLE502Media source unavailable

GET /v1/stream/* is not rate-limited, so video playback is never interrupted.

Error codes

CodeStatusMeaning
INVALID_QUERY400Missing/invalid q on /v1/search
INVALID_ID400Malformed :slug
INVALID_SEASON400Malformed season number
INVALID_EPISODE400Malformed episode number
TOKEN_MISSING400/v1/stream/:token without a token
TOKEN_INVALID403Forged, malformed or invalid link
TOKEN_EXPIRED403Link older than 6 hours
NOT_FOUND404Route or resource does not exist
RATE_LIMITED429Too many requests, see Rate limiting
INTERNAL_ERROR500Unhandled server error
UPSTREAM_UNAVAILABLE502Media source could not be reached
API_DISABLED503API is temporarily switched off

Rate limiting

Applies per IP (CF-Connecting-IP) to /v1/search, /v1/anime/* and /health (default 60 requests / 60 seconds). /v1/stream/* is exempt.

Every limited response carries the current budget:

HeaderDescription
X-RateLimit-LimitMax requests per window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets

When exhausted:

HTTP/1.1 429
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1786969800
{
  "success": false,
  "error": { "code": "RATE_LIMITED", "message": "Too many requests. Please slow down.", "status": 429 },
  "meta": { }
}

Retry after X-RateLimit-Reset.

CORS

CORS is disabled by default — browser-based clients must be allowlisted by the API operator. When enabled, responses include Access-Control-Allow-Origin for allowed origins, and preflight OPTIONS is answered with 204.

Pagination

Episode lists use page (default 1, max 1000) and per_page (default 20, max 100). Both are silently clamped to their valid ranges. data.total is the full episode count regardless of pagination.

Examples

Search:

curl "https://openanime-api.ewgsta.me/v1/search?q=angel%20beats"

Series details:

curl "https://openanime-api.ewgsta.me/v1/anime/angel-beats"

Episode list (page 2, 10 per page):

curl "https://openanime-api.ewgsta.me/v1/anime/angel-beats/seasons/1/episodes?page=2&per_page=10"

Single episode:

curl "https://openanime-api.ewgsta.me/v1/anime/angel-beats/seasons/1/episodes/1"

Stream the first 1 MB of an episode to a file:

TOKEN=$(curl -s ".../episodes/1" | jq -r '.data.episode.sources[0].stream_url')
curl -L "$TOKEN" -o episode.mp4

Resume / seek (Range):

curl -L -r 1048576- "$TOKEN" -o episode_part2.mp4

Health:

curl "https://openanime-api.ewgsta.me/health"

On this page