Provider-specific Docs · v1.5.0

Ani.pm Provider

Wraps ani.pm and scrapes every server the site exposes. Unlike other providers that just grab one stream, anipm enumerates all 4 server types per episode — Vega (MP4 file server), Onyx (HLS master with 1080p/720p/360p variants), Vidnest (iframe embed), and MegaPlay (HLS via megaplay.buzz). Every URL, every CDN host, every server entry is exposed in the raw payload — no filtering, no "if it plays" gating.

New in v1.5.0: /api/scrape/anipm-raw — a deep raw scrape endpoint that actually probes every server. HLS playlists are fetched and parsed (variant count, segment count, duration, raw m3u8 text), MP4 files are HEAD-probed for size + Range support, iframe URLs are recorded as-is. Not just iframe shit.
Server Types
4
Vega · Onyx · Vidnest · MegaPlay
Stream Formats
3
HLS · MP4 · iframe
Audio
Sub + Dub
Per-episode variants
Priority
HLS → MP4 → iframe
m3u8 first, iframe last

Overview

Ani.pm is a React SPA backed by an Express-style REST API at https://ani.pm/api/. Cloudflare fronts the whole origin, so every call needs full browser headers (Sec-Ch-Ua, Sec-Fetch-*, Origin) or Cloudflare returns a 403 managed-challenge page. The provider handles this internally via curl with the full browser header set.

The provider's standout feature is server enumeration: instead of picking one stream and hiding the rest, it returns every server ani.pm exposes, normalized into the unified source list AND exposed verbatim in the raw payload. This lets clients pick the best stream per context (HLS for adaptive quality, MP4 for offline download, iframe for fallback when Cloudflare blocks direct m3u8 fetches).

Source priority: HLS master playlists first (most reliable, adaptive quality), MP4 files second (direct download, range-supported), iframe embeds last (fallback only — browser solves any Cloudflare challenge natively). Per user instruction: "import m3u8 mainly and mp4 ... dont need if playing or not" — every server is included regardless of whether we can verify it'll play.

ID Format

The anipm provider uses a composite id format that encodes both the ani.pm numeric series id (used for /api/anime/series/{id}) and the slug (used for /api/anime/src/servers?title={slug}).

Accepted id formatstext
anipm:6351:frieren-beyond-journey-end   ← full format (recommended)
anipm:6351                                ← series id only (slug looked up via cache)
6351                                      ← bare numeric (treated as series id)
al:154587                                 ← universal AniList id (auto-resolved)

The universal al:{anilistId} format works on anipm too — the backend looks up the AniList title, searches ani.pm, and matches automatically. Use the ID Finder to inspect what native id the resolver picked.

Server Types

Ani.pm exposes 4 distinct server types per episode. The provider scrapes all of them and exposes each as a separate UnifiedStreamSource entry.

Vegakind: filetype: mp4

Direct MP4 file server. ani.pm serves ~251MB MP4 files with HTTP Range support — perfect for direct download or <video> playback without HLS. Wrapped through the CORS proxy for CORS headers + Range passthrough.

Upstream: /api/anime/src/file?t={token}
Onyxkind: hlstype: master

HLS master playlist with 1080p/720p/360p quality variants. The upstream m3u8 uses relative URIs that need rewriting — the proxy handles this automatically. Cloudflare-fronted, needs Referer: https://ani.pm/.

Upstream: /api/anime/src/hls?t={token}
Vidnestkind: embedtype: iframe

Iframe embed at vidnest.fun. Last-resort fallback — the browser solves any Cloudflare challenge natively. Not proxied; passed through as-is.

Upstream: https://vidnest.fun/anime/...
MegaPlaykind: hlstype: master

HLS via megaplay.buzz (same pipeline as the anilight provider). The series doc gives us sub/dub embed URLs for every episode; we probe megaplay's API, extract the m3u8 + subtitles + intro/outro skip markers. Most reliable stream — Cloudflare doesn't challenge megaplay.buzz's CDN.

Upstream: https://megaplay.buzz/stream/getSourcesNew?id={episode_id}
All servers, all the time. The provider does not filter servers based on playability, quality, or assumed availability. If ani.pm returns a server, it's in the response. The player decides what works.

Raw Payload Schema

The raw field on the /sources response contains the full upstream payload — every server ani.pm returned, every URL we resolved, every CDN host we touched. Returned verbatim so developers can inspect exactly what ani.pm gave us.

raw payload (annotated)json
{
  "provider": "anipm",
  "api": {
    "series": "https://ani.pm/api/anime/series/6351",
    "servers": "https://ani.pm/api/anime/src/servers?title=frieren-beyond-journey-end&ep=1",
    "megaplay_variants": "https://megaplay.buzz/api/12345",
    "megaplay_sources": "https://megaplay.buzz/stream/getSourcesNew?id={episode_id}"
  },
  "animeId": "anipm:6351:frieren-beyond-journey-end",
  "episodeNumber": 1,
  "server": "onyx-hls",
  "sourceType": "sub",
  "seriesId": 6351,
  "slug": "frieren-beyond-journey-end",

  "upstream_servers": {
    "sub": [
      { "provider": "Vega",   "kind": "file",  "url": "/api/anime/src/file?t=abc...", "priority": 1 },
      { "provider": "Onyx",   "kind": "hls",   "url": "/api/anime/src/hls?t=def...",  "priority": 2 },
      { "provider": "Vidnest","kind": "embed", "url": "https://vidnest.fun/anime/...", "priority": 3 }
    ],
    "dub": [
      { "provider": "Vega",   "kind": "file",  "url": "/api/anime/src/file?t=ghi..." }
    ]
  },

  "servers_scraped": {
    "hls": [
      {
        "url": "https://ani.pm/api/anime/src/hls?t=def...",
        "proxied_url": "/api/proxy/m3u8?url=https%3A%2F%2Fani.pm%2F...",
        "referer": "https://ani.pm/",
        "kind": "anipm-onyx"
      },
      {
        "url": "https://*.nekostream.site/master.m3u8",
        "proxied_url": "/api/proxy/m3u8?url=...&referer=https%3A%2F%2Fmegaplay.buzz%2F",
        "referer": "https://megaplay.buzz/",
        "kind": "megaplay"
      }
    ],
    "mp4": [
      {
        "url": "https://ani.pm/api/anime/src/file?t=abc...",
        "proxied_url": "/api/proxy/m3u8?url=...",
        "referer": "https://ani.pm/"
      }
    ],
    "iframe": [
      {
        "url": "https://vidnest.fun/anime/...",
        "referer": "https://ani.pm/"
      },
      {
        "url": "https://megaplay.buzz/stream/s-2/12345/sub",
        "referer": "https://ani.pm/"
      }
    ]
  },

  "megaplay": {
    "embedUrl": "https://megaplay.buzz/stream/s-2/12345/sub",
    "variantType": "sub",
    "m3u8": "https://*.nekostream.site/master.m3u8",
    "proxied_m3u8": "/api/proxy/m3u8?url=...&referer=https%3A%2F%2Fmegaplay.buzz%2F",
    "subtitles": [
      { "url": "/api/proxy/m3u8?format=vtt&url=...", "lang": "English" }
    ],
    "intro": { "start": 5, "end": 85 },
    "outro": { "start": 1380, "end": 1440 }
  },

  "normalized": {
    "anilist_id": 6351,
    "episode": 1,
    "stream_type": "sub",
    "providers_scraped": [
      "Vega/file/sub",
      "Onyx/hls/sub",
      "Vidnest/embed/sub",
      "Vega/file/dub",
      "megaplay/hls"
    ],
    "cdn_hosts": [
      "ani.pm",
      "vidnest.fun",
      "*.nekostream.site",
      "megaplay.buzz"
    ],
    "source_counts": { "hls": 2, "mp4": 1, "iframe": 2, "total": 5 },
    "is_default": true
  }
}

Get anime info

Fetches the full ani.pm series document (GET /api/anime/series/{id}) and returns the unified metadata. Auto-enriched with AniList data when anilistId is exposed.

GET/api/scrape/info?id={animeId}&provider=anipm
ParameterTypeRequiredDefaultDescription
idstringrequiredanipm:{seriesId}:{slug}, anipm:{seriesId}, or al:{anilistId}.
providerenumoptionalanimetsuMust be 'anipm'.
enrich0|1optional1Set to 0 to skip AniList enrichment.
Requestbash
curl "https://your-deployment.example.com/api/scrape/info?id=al:154587&provider=anipm"

Get episode list

Returns the full episode list from the ani.pm series document. Each episode's sourceId encodes the megaplay sub/dub embed URLs (used internally by /sources).

GET/api/scrape/episodes?id={animeId}&provider=anipm
Requestbash
curl "https://your-deployment.example.com/api/scrape/episodes?id=al:154587&provider=anipm"
200 OK (truncated)json
[
  {
    "number": 1,
    "displayNumber": "1",
    "sourceId": "6351|1|https://megaplay.buzz/stream/s-2/12345/sub|https://megaplay.buzz/stream/s-2/12345/dub",
    "title": "The Journey's End",
    "thumbnail": "https://ani.pm/cdn/episodes/...",
    "variants": ["sub", "dub"]
  }
]

Get streaming servers

Enumerates the servers ani.pm exposes for the episode (via /api/anime/src/servers). Always includes 'megaplay' as a synthetic server because the series doc carries megaplay embed URLs for every episode.

GET/api/scrape/servers?id={animeId}&ep={episode}&provider=anipm
Requestbash
curl "https://your-deployment.example.com/api/scrape/servers?id=al:154587&ep=1&provider=anipm"
200 OKjson
[
  { "id": "vega-file-sub",   "label": "Vega SUB (file)",   "description": "Vega · file · sub",   "default": true },
  { "id": "onyx-hls-sub",    "label": "Onyx SUB (hls)",    "description": "Onyx · hls · sub" },
  { "id": "vidnest-embed-sub","label": "Vidnest SUB (embed)","description": "Vidnest · embed · sub" },
  { "id": "vega-file-dub",   "label": "Vega DUB (file)",   "description": "Vega · file · dub" },
  { "id": "megaplay",        "label": "MegaPlay",          "description": "megaplay.buzz HLS — same pipeline as the anilight provider" }
]

Get stream sources (ALL servers)

The main endpoint. Returns every server ani.pm exposes — HLS master playlists first, MP4 files second, iframe embeds last. Includes subtitles and intro/outro skip markers from MegaPlay. The raw field contains the full upstream payload (see Raw Payload Schema above).

GET/api/scrape/sources?id={animeId}&ep={episode}&server={server}&type={sub|dub}&provider=anipm
ParameterTypeRequiredDefaultDescription
idstringrequiredanipm id or al:{anilistId}.
epnumberrequiredEpisode number (1-indexed).
serverstringoptionalonyx-hlsServer id from /servers (informational — all servers are returned regardless).
typeenumoptionalsubsub or dub. When dub is requested but unavailable, falls back to sub.
providerenumoptionalanimetsuMust be 'anipm'.
curl "https://your-deployment.example.com/api/scrape/sources?id=al:154587&ep=1&provider=anipm"
200 OK (truncated — see Raw Payload Schema for full raw field)json
{
  "sources": [
    {
      "url": "/api/proxy/m3u8?url=https%3A%2F%2Fmegaplay.buzz%2F...master.m3u8&referer=https%3A%2F%2Fmegaplay.buzz%2F",
      "type": "master",
      "quality": "auto",
      "isMaster": true,
      "originalUrl": "https://*.nekostream.site/master.m3u8",
      "upstreamReferer": "https://megaplay.buzz/"
    },
    {
      "url": "/api/proxy/m3u8?url=https%3A%2F%2Fani.pm%2Fapi%2Fanime%2Fsrc%2Fhls%3Ft%3Ddef...&referer=https%3A%2F%2Fani.pm%2F",
      "type": "master",
      "quality": "auto",
      "isMaster": true,
      "originalUrl": "https://ani.pm/api/anime/src/hls?t=def...",
      "upstreamReferer": "https://ani.pm/"
    },
    {
      "url": "/api/proxy/m3u8?url=https%3A%2F%2Fani.pm%2Fapi%2Fanime%2Fsrc%2Ffile%3Ft%3Dabc...&referer=https%3A%2F%2Fani.pm%2F",
      "type": "mp4",
      "quality": "1080p",
      "originalUrl": "https://ani.pm/api/anime/src/file?t=abc...",
      "upstreamReferer": "https://ani.pm/"
    },
    {
      "url": "https://vidnest.fun/anime/...",
      "type": "iframe",
      "quality": "auto",
      "originalUrl": "https://vidnest.fun/anime/...",
      "upstreamReferer": "https://ani.pm/"
    }
  ],
  "subtitles": [
    { "url": "/api/proxy/m3u8?format=vtt&url=...", "lang": "English" }
  ],
  "skips": { "intro": { "start": 5, "end": 85 }, "outro": { "start": 1380, "end": 1440 } },
  "server": "onyx-hls",
  "provider": "anipm",
  "raw": { /* see Raw Payload Schema */ }
}

Get raw upstream payload only

Returns ONLY the raw upstream payload — the original JSON from ani.pm's API, before any normalization. Useful when you want to inspect exactly what ani.pm returned without the unified source wrapping. Does NOT probe the upstream servers — just records what ani.pm's API returned.

GET/api/scrape/raw?id={animeId}&ep={episode}&provider=anipm
Requestbash
curl "https://your-deployment.example.com/api/scrape/raw?id=al:154587&ep=1&provider=anipm"

Response shape: { provider, animeId, requestedId, episode, server, streamType, raw, rawMulti, unified }. The raw field matches the schema documented in the Raw Payload Schema section above. The unified field also includes the player-ready sources for convenience. For a DEEP scrape that actually probes every server URL, use /api/scrape/anipm-raw instead.

Deep raw scrape — probe EVERY server

The headline endpoint. For every server anipm exposes on the episode, we actually fetch the upstream URL and return what came back. HLS playlists are pulled and parsed (variant count, segment count, total duration, raw m3u8 text), MP4 files are HEAD-probed for Content-Length + Accept-Ranges + Last-Modified, iframe URLs are recorded as-is. No "if it plays or not" gating, no iframe-only cop-out. Per user instruction: "make scrape that scrape all server raw data all server not iframe shit".

GET/api/scrape/anipm-raw?id={animeId}&ep={episode}&type={sub|dub}
ParameterTypeRequiredDefaultDescription
idstringrequiredanipm:{seriesId}:{slug}, anipm:{seriesId}, or al:{anilistId} (universal — resolved via title search).
epnumberrequiredEpisode number (1-indexed).
typeenumoptionalsubsub or dub. When dub is requested but unavailable, falls back to sub for the megaplay variant.
serverstringoptionalOptional — accepted but ignored. We probe ALL servers regardless of this parameter.
curl "https://your-deployment.example.com/api/scrape/anipm-raw?id=al:154587&ep=1" | jq '.servers_grouped.counts'

Per-server probe shape

Each entry in the servers array has a different shape depending on its type. The common fields are index, type, kind, url (proxied), upstream_url, upstream_referer, quality. The type-specific fields are:

TypeKindExtra fields returned
masteranipm-onyx-hls, megaplay-hlshttp_status, content_type, raw_m3u8, raw_m3u8_truncated, is_master, variant_count, segment_count, duration_seconds, first_variant_url, first_segment_url
mp4filehttp_status, content_type, content_length, accept_ranges, last_modified, etag
iframeiframenote (iframe URLs are not probed server-side — the user's browser solves any Cloudflare challenge natively)

Response example (truncated)

200 OK — Frieren ep 1, subjson
{
  "provider": "anipm",
  "endpoint": "/api/scrape/anipm-raw",
  "description": "Deep raw scrape — every server probed, m3u8 content fetched, MP4 headers captured, iframe URLs recorded. Not just iframe shit.",
  "requestedId": "al:154587",
  "animeId": "anipm:6351:",
  "episode": 1,
  "streamType": "sub",
  "server": "auto",

  "servers": [
    {
      "index": 0,
      "type": "master",
      "kind": "megaplay-hls",
      "url": "/api/proxy/m3u8?url=https%3A%2F%2F9hjkrt.nekostream.site%2F...master.m3u8&referer=https%3A%2F%2Fmegaplay.buzz%2F",
      "upstream_url": "https://9hjkrt.nekostream.site/bb6d2babd7797d94d8f4a8600bc9b44e/.../master.m3u8",
      "upstream_referer": "https://megaplay.buzz/",
      "quality": "auto",
      "http_status": 200,
      "content_type": "application/vnd.apple.mpegurl",
      "raw_m3u8": "#EXTM3U\n#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1800112,RESOLUTION=1920x1080,FRAME-RATE=25.000,CODECS=\"avc1.640032,mp4a.40.2\"\nindex-f1-v1-a1.m3u8\n#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=49071,...",
      "raw_m3u8_truncated": false,
      "is_master": true,
      "variant_count": 1,
      "segment_count": 0,
      "duration_seconds": null,
      "first_variant_url": "https://9hjkrt.nekostream.site/.../index-f1-v1-a1.m3u8",
      "first_segment_url": null
    },
    {
      "index": 1,
      "type": "master",
      "kind": "anipm-onyx-hls",
      "url": "/api/proxy/m3u8?url=https%3A%2F%2Fani.pm%2Fapi%2Fanime%2Fsrc%2Fhls%3Ft%3D...&referer=https%3A%2F%2Fani.pm%2F",
      "upstream_url": "https://ani.pm/api/anime/src/hls?t=0xfi2w1aVS37BzKE75nBnjxfUCPZEvN_6yPRZQjmRFfGARxKHBKcveX8BFZ",
      "upstream_referer": "https://ani.pm/",
      "http_status": 200,
      "content_type": "application/vnd.apple.mpegurl; charset=utf-8",
      "is_master": true,
      "variant_count": 3,
      "segment_count": 0,
      "first_variant_url": "https://ani.pm/api/anime/src/hls?t=4_oxzZk-6LniwRnaZuyX3VXmsDxH51vNLWz_B3CVa-LilSEZ1RP_pzQ"
    },
    {
      "index": 3,
      "type": "mp4",
      "kind": "file",
      "url": "/api/proxy/m3u8?url=https%3A%2F%2Fani.pm%2Fapi%2Fanime%2Fsrc%2Ffile%3Ft%3D...&referer=https%3A%2F%2Fani.pm%2F",
      "upstream_url": "https://ani.pm/api/anime/src/file?t=5snl9KU5Z5Wn5-tfNtRPKdxsHY4wdF6U1UVUlloyA-cQBYRmAiKc2BHTLt",
      "upstream_referer": "https://ani.pm/",
      "quality": "1080p",
      "http_status": 200,
      "content_type": "video/mp4",
      "content_length": 251568229,
      "accept_ranges": "bytes",
      "last_modified": "Mon, 15 Jan 2024 09:23:41 GMT",
      "etag": "\"abc123\""
    },
    {
      "index": 4,
      "type": "iframe",
      "kind": "iframe",
      "url": "https://vidnest.fun/anime/52991/1/sub",
      "upstream_url": "https://vidnest.fun/anime/52991/1/sub",
      "upstream_referer": "https://ani.pm/",
      "note": "iframe — browser solves Cloudflare challenge natively; not probed server-side"
    }
  ],

  "servers_grouped": {
    "hls":   [ /* ...3 entries... */ ],
    "mp4":   [ /* ...1 entry...  */ ],
    "iframe":[ /* ...2 entries.. */ ],
    "counts": { "hls": 3, "mp4": 1, "iframe": 2, "total": 6 }
  },

  "upstream_servers": {
    "sub": [
      { "provider": "Vega",   "kind": "file",  "url": "/api/anime/src/file?t=...",  "priority": 1 },
      { "provider": "Onyx",   "kind": "hls",   "url": "/api/anime/src/hls?t=...",   "priority": 2 },
      { "provider": "Vega",   "kind": "embed", "url": "https://vidnest.fun/anime/52991/1/sub" }
    ],
    "dub": [
      { "provider": "Onyx",   "kind": "hls",   "url": "/api/anime/src/hls?t=..." }
    ]
  },

  "megaplay": {
    "embedUrl": "https://megaplay.buzz/stream/s-2/107257/sub",
    "variantType": "sub",
    "m3u8": "https://9hjkrt.nekostream.site/.../master.m3u8",
    "proxied_m3u8": "/api/proxy/m3u8?url=...&referer=https%3A%2F%2Fmegaplay.buzz%2F",
    "subtitles": [ { "url": "/api/proxy/m3u8?format=vtt&url=...", "lang": "English" } ],
    "intro": { "start": 5, "end": 85 },
    "outro": { "start": 1380, "end": 1440 }
  },

  "subtitles": [ /* same as megaplay.subtitles */ ],
  "skips": { "intro": { "start": 5, "end": 85 }, "outro": { "start": 1380, "end": 1440 } },

  "unified_sources": [ /* same shape as /api/scrape/sources sources[] */ ],
  "api": {
    "series": "https://ani.pm/api/anime/series/6351",
    "servers": "https://ani.pm/api/anime/src/servers?title=frieren-beyond-journey-s-end-c6fbj&ep=1",
    "megaplay_variants": "https://megaplay.buzz/api/107257",
    "megaplay_sources": "https://megaplay.buzz/stream/getSourcesNew?id={episode_id}"
  }
}
Why this exists: The plain /api/scrape/raw endpoint returns the raw upstream payload, but it doesn't actually probe the server URLs — it just records what ani.pm's API returned. This endpoint goes one level deeper: for every HLS URL, we fetch the m3u8 and parse it. For every MP4 URL, we HEAD-probe it. So you can see at a glance "this Onyx master has 3 variants (1080p, 720p, 360p)" or "this Vega MP4 is 239MB and supports Range requests" — without having to fetch each URL yourself.

Endpoint Comparison

Three endpoints touch stream data. Here's exactly what each one returns so you can pick the right one for your use case.

EndpointProbes servers?Returns raw m3u8?Returns MP4 headers?Best for
/api/scrape/sourcesNoNo (URLs only)NoPlayer playback — returns proxied URLs ready for <video> / hls.js
/api/scrape/rawNoNo (URLs only)NoInspecting what ani.pm's API returned (server list, megaplay diagnostics)
/api/scrape/anipm-rawYESYESYESDeep inspection — every server actually fetched, m3u8 parsed, MP4 HEAD-probed
Latency note: /sources and /raw both complete in ~1-2s (one round-trip to ani.pm + megaplay). /anipm-raw takes ~3-6s because it fetches every HLS playlist + HEAD-probes every MP4 in parallel. Don't call it on every page load — call it once when you need to inspect what's available, then cache.

Upstream API

The provider wraps these ani.pm REST endpoints. All are Cloudflare-fronted GET requests; the provider uses curl with full browser headers (Sec-Ch-Ua, Sec-Fetch-*, Origin) to bypass the managed challenge.

EndpointPurposeReturns
/api/anime/search?q=Search catalog{ items: AnipmSearchItem[] }
/api/anime/series/{id}Full anime docAnipmSeries (incl. episodes[])
/api/anime/src/servers?title={slug}&ep={n}Enumerate servers{ sub: [...], dub: [...] }
/api/anime/src/hls?t=Onyx HLS masterm3u8 (relative URIs!)
/api/anime/src/file?t=Vega MP4 file251MB MP4 (Range-supported)
megaplay.buzz/api/{realid}MegaPlay variants{ success, data: MegaplayVariant[] }
megaplay.buzz/stream/getSourcesNew?id=MegaPlay HLS + subs + skips{ sources: {file}, tracks, intro, outro }

CDN Hosts

The provider touches these CDN hosts. All non-iframe URLs are wrapped through the CORS proxy with the appropriate Referer header.

ani.pm
API + Vega MP4 + Onyx HLS
Referer: https://ani.pm/
vidnest.fun
Vidnest iframe embeds
Referer: (passthrough — browser solves challenge)
megaplay.buzz
MegaPlay API
Referer: https://ani.pm/
*.nekostream.site
MegaPlay HLS m3u8 + variant segments
Referer: https://megaplay.buzz/

Cloudflare Bypass

Ani.pm is fully behind Cloudflare's managed challenge. Node's undici (used by fetch()) gets a 403 on the series/{id} and src/hls endpoints due to TLS fingerprinting. The provider works around this by shelling out to curl with the full browser header set:

The exact curl invocation used internallybash
curl -sSL \
  -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
  -H "Referer: https://ani.pm/" \
  -H "Origin: https://ani.pm" \
  -H "Accept: application/json,text/plain,*/*;q=0.8" \
  -H "Accept-Language: en-US,en;q=0.9" \
  -H 'Sec-Ch-Ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H "Sec-Ch-Ua-Mobile: ?0" \
  -H 'Sec-Ch-Ua-Platform: "Windows"' \
  -H "Sec-Fetch-Dest: empty" \
  -H "Sec-Fetch-Mode: cors" \
  -H "Sec-Fetch-Site: same-origin" \
  --http2 \
  --max-time 20 \
  "https://ani.pm/api/anime/series/6351"

The MegaPlay endpoints (megaplay.buzz/api/* and megaplay.buzz/stream/getSourcesNew) do not need curl — Node's fetch works fine because Cloudflare doesn't challenge megaplay.buzz's CDN. The provider uses fetch for those, which is faster.