{"id":29710724,"url":"https://github.com/shineii86/aninewsapi","last_synced_at":"2026-05-27T21:00:48.008Z","repository":{"id":304833855,"uuid":"1020168548","full_name":"Shineii86/AniNewsAPI","owner":"Shineii86","description":"Powerful Anime News API - real-time aggregation from 7 sources with smart caching, keyword search, RSS feeds, and full-article extraction.","archived":false,"fork":false,"pushed_at":"2026-05-27T19:55:55.000Z","size":27975,"stargazers_count":20,"open_issues_count":0,"forks_count":9,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T20:13:42.729Z","etag":null,"topics":["anime","anime-news","crunchyroll","javascript","manga","myanimelist","news-api","rest-api","rss-feed","serverless","vercel","web-scraping"],"latest_commit_sha":null,"homepage":"https://aninews.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Shineii86.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-15T12:52:15.000Z","updated_at":"2026-05-27T19:46:27.000Z","dependencies_parsed_at":"2025-07-16T08:37:52.782Z","dependency_job_id":"c3333ac5-0c5b-4f6f-a2e7-60b19d401f5e","html_url":"https://github.com/Shineii86/AniNewsAPI","commit_stats":null,"previous_names":["shineii86/aninews"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/Shineii86/AniNewsAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shineii86%2FAniNewsAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shineii86%2FAniNewsAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shineii86%2FAniNewsAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shineii86%2FAniNewsAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Shineii86","download_url":"https://codeload.github.com/Shineii86/AniNewsAPI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shineii86%2FAniNewsAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33583399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anime","anime-news","crunchyroll","javascript","manga","myanimelist","news-api","rest-api","rss-feed","serverless","vercel","web-scraping"],"created_at":"2025-07-23T21:39:48.267Z","updated_at":"2026-05-27T21:00:47.995Z","avatar_url":"https://github.com/Shineii86.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [!NOTE]\n\u003e The Old Deployment URL `https://aninewsapi.vercel.app` Is No Longer Accessible. Use The Current URL: **https://aninews.vercel.app**\n\n\u003cdiv align=\"center\"\u003e\n  \n  \u003cimg src=\"https://capsule-render.vercel.app/api?type=waving\u0026height=300\u0026color=gradient\u0026text=𝗔𝗻𝗶𝗡𝗲𝘄𝘀𝗔𝗣𝗜\u0026fontAlignY=30\u0026fontSize=90\u0026desc=𝖱𝖾𝖺𝗅-𝖳𝗂𝗆𝖾%20𝖠𝗇𝗂𝗆𝖾%20𝖭𝖾𝗐𝗌%20𝖠𝗀𝗀𝗋𝖾𝗀𝖺𝗍𝗂𝗈𝗇%20𝖠𝖯𝖨\u0026descSize=30\" /\u003e\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/stargazers\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=github\u0026color=f43f8e\u0026logoColor=white\" alt=\"Stars\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/network/members\"\u003e\u003cimg src=\"https://img.shields.io/github/forks/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=github\u0026color=a855f7\u0026logoColor=white\" alt=\"Forks\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/issues\"\u003e\u003cimg src=\"https://img.shields.io/github/issues/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=github\u0026color=7c3aed\u0026logoColor=white\" alt=\"Issues\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/pulls\"\u003e\u003cimg src=\"https://img.shields.io/github/issues-pr/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=github\u0026color=ec4899\u0026logoColor=white\" alt=\"Pull Requests\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/commits\"\u003e\u003cimg src=\"https://img.shields.io/github/last-commit/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=github\u0026color=6366f1\u0026logoColor=white\" alt=\"Last Commit\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/Shineii86/AniNewsAPI?style=for-the-badge\u0026logo=mit\u0026color=22c55e\u0026logoColor=white\" alt=\"License\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Node.js-20-339933?style=flat-square\u0026logo=node.js\u0026logoColor=white\" alt=\"Node.js\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Express-5.1-000000?style=flat-square\u0026logo=express\u0026logoColor=white\" alt=\"Express\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Vercel-Serverless-000000?style=flat-square\u0026logo=vercel\u0026logoColor=white\" alt=\"Vercel\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-22c55e?style=flat-square\u0026logo=mit\u0026logoColor=white\" alt=\"License\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Version-4.2.0-f43f8e?style=flat-square\u0026logoColor=white\" alt=\"Version\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Sources-7-a855f7?style=flat-square\u0026logoColor=white\" alt=\"Sources\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Endpoints-12-6366f1?style=flat-square\u0026logoColor=white\" alt=\"Endpoints\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eA serverless API aggregating anime news from 7 sources in real-time.\u003c/b\u003e\u003cbr/\u003e\n  Smart caching, keyword search, RSS feeds, date filtering, cursor pagination, and source health monitoring.\u003cbr/\u003e\n  Built for speed, reliability, and the anime community.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#-table-of-contents\"\u003eTable of Contents\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#-features\"\u003eFeatures\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#-api-endpoints\"\u003eAPI Docs\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#-quick-start\"\u003eQuick Start\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#-deployment\"\u003eDeployment\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#-contributing\"\u003eContributing\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## 📖 Table of Contents\n\n- [Overview](#-overview)\n- [Features](#-features)\n- [News Sources](#-news-sources)\n- [Tech Stack](#-tech-stack)\n- [Architecture](#-architecture)\n- [Project Structure](#-project-structure)\n- [Quick Start](#-quick-start)\n- [Configuration](#-configuration)\n- [API Endpoints](#-api-endpoints)\n- [API Response Schema](#-api-response-schema)\n- [Deployment](#-deployment)\n- [Available Scripts](#-available-scripts)\n- [Performance](#-performance)\n- [Changelog Highlights](#-changelog-highlights)\n- [Troubleshooting](#-troubleshooting)\n- [FAQ](#-faq)\n- [Roadmap](#-roadmap)\n- [Contributing](#-contributing)\n- [Acknowledgements](#-acknowledgements)\n- [License](#-license)\n- [Author](#-author)\n- [Star History](#-star-history)\n\n---\n\n## 🌸 Overview\n\n**AniNewsAPI** is a serverless anime news aggregation API that scrapes, deduplicates, and serves articles from **7 major anime news sources** — all through a clean REST API with zero database setup.\n\n\u003e 💡 No database, no auth for reads, no complex setup. Just deploy to Vercel and you have a production API.\n\n### Why AniNewsAPI?\n\n- 📰 **7 Sources** — ANN, MAL, Crunchyroll, Anime Corner, Otaku USA, Anime Herald, Comic Book\n- ⚡ **Smart Caching** — Two-tier cache (memory + disk) with 10-minute TTL, survives serverless cold starts\n- 🔍 **Full-Text Search** — Relevance-scored search across titles, excerpts, sources, and tags\n- 🗞️ **RSS Feeds** — Standards-compliant RSS 2.0 for any feed reader\n- 📄 **Full Article Extraction** — Get readable article content by slug\n- 🏷️ **Tag Filtering** — Browse articles by tag with count aggregation\n- 📊 **Source Health** — Real-time per-source health checks, latency, and article counts\n- 🔒 **CORS Enabled** — Works from any frontend, no proxy needed\n- 🚀 **Zero-Config Deploy** — One click to Vercel, or run standalone with Express\n\n### How It Works\n\n```mermaid\nflowchart TD\n    A[\"🌐 Client Request\u003cbr/\u003e(Browser / App / curl)\"] --\u003e B[\"🛡️ Vercel Edge / Express Server\u003cbr/\u003eCORS · Security Headers · Rate Limiting\"]\n    B --\u003e C{\"💾 Cache Check\u003cbr/\u003e(node-cache + disk)\"}\n    C -- HIT --\u003e D[\"⚡ Return Cached Response\u003cbr/\u003e~200ms\"]\n    C -- MISS --\u003e E[\"📰 7 Concurrent Fetchers\"]\n\n    E --\u003e E1[\"ANN\"]\n    E --\u003e E2[\"MAL\"]\n    E --\u003e E3[\"Crunchyroll\"]\n    E --\u003e E4[\"Anime Corner\"]\n    E --\u003e E5[\"Otaku USA\"]\n    E --\u003e E6[\"Anime Herald\"]\n    E --\u003e E7[\"Comic Book\"]\n\n    E1 \u0026 E2 \u0026 E3 \u0026 E4 \u0026 E5 \u0026 E6 \u0026 E7 --\u003e F[\"🔄 RSS / Google News RSS / Web Scraping\u003cbr/\u003e3 retries · 15s timeout · exponential backoff\"]\n    F --\u003e G[\"🧹 Deduplicate · Enrich · Cache\"]\n    G --\u003e H[\"📤 Respond\u003cbr/\u003eJSON · RSS 2.0 XML · SSE\"]\n\n    style A fill:#1e1e2e,stroke:#a78bfa,color:#f1f5f9\n    style B fill:#1e1e2e,stroke:#6366f1,color:#f1f5f9\n    style C fill:#1e1e2e,stroke:#f43f8e,color:#f1f5f9\n    style D fill:#1e1e2e,stroke:#22c55e,color:#f1f5f9\n    style E fill:#1e1e2e,stroke:#a855f7,color:#f1f5f9\n    style F fill:#1e1e2e,stroke:#eab308,color:#f1f5f9\n    style G fill:#1e1e2e,stroke:#06b6d4,color:#f1f5f9\n    style H fill:#1e1e2e,stroke:#22c55e,color:#f1f5f9\n```\n\n---\n\n## ✨ Features\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n\n### ⚡ Core\n- **Real-time scraping** from 7 anime news sources\n- **Smart caching** with 10-minute TTL + disk backup\n- **Concurrent fetching** — all sources hit simultaneously\n- **Retry logic** — 3 attempts per source with exponential backoff\n- **Graceful degradation** — if a source fails, others continue\n- **Google News RSS proxy** for Cloudflare-protected sources\n\n    \u003c/td\u003e\n    \u003ctd\u003e\n\n### 🔍 Data\n- **Keyword search** with relevance scoring (`/api/search`)\n- **Date range filtering** — `?from=YYYY-MM-DD\u0026to=YYYY-MM-DD`\n- **Cursor pagination** — opaque `nextCursor` for efficient paging\n- **RSS 2.0 feed** for readers \u0026 integrations (`/api/rss`)\n- **Full article extraction** by slug (`/api/news/:slug`)\n- **Tag filtering** with article counts (`/api/news/tags`)\n\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n\n### 🛡️ Reliability\n- **RSS fallback** when web scraping is blocked\n- **Cross-source deduplication** by normalized title\n- **Timeout protection** — 15s per source, never hangs\n- **CORS enabled** — works from any frontend\n- **Security headers** — X-Frame-Options, X-Content-Type-Options\n- **Rate limiting** — 100 req/min per IP with headers\n\n    \u003c/td\u003e\n    \u003ctd\u003e\n\n### 📊 Monitoring\n- **Source health** — per-source status, article count, latency (`/api/sources`)\n- **Cache statistics** — hit/miss metrics (`/api/stats`)\n- **Health check** — uptime, version, node info (`/api/health`)\n- **Cache clear auth** — API key protected (`CACHE_CLEAR_KEY` env)\n- **OpenAPI 3.0.3 spec** — machine-readable API definition\n\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### 🌟 Feature Highlights\n\n| Feature | Description | Status |\n|:---|:---|:---:|\n| 📰 7 News Sources | ANN, MAL, Crunchyroll, Anime Corner, Otaku USA, Anime Herald, Comic Book | ✅ |\n| ⚡ Smart Caching | Two-tier (memory + disk) with 10-min TTL | ✅ |\n| 🔍 Full-Text Search | Relevance scoring — title (10pts) vs excerpt (3pts) | ✅ |\n| 📄 Article Extraction | Full content parsing from original URLs | ✅ |\n| 🗞️ RSS 2.0 Feed | Standards-compliant XML with media:thumbnail | ✅ |\n| 📅 Date Filtering | `?from=YYYY-MM-DD\u0026to=YYYY-MM-DD` on news \u0026 search | ✅ |\n| 🔄 Cursor Pagination | Opaque base64url cursors for stable paging | ✅ |\n| 🏷️ Tag System | Tag listing with counts, filter by tag | ✅ |\n| 📊 Source Health | Real-time fetch status, latency, article counts | ✅ |\n| 🔒 Cache Auth | API key protection for cache clear endpoint | ✅ |\n| 📡 SSE Stream | Server-Sent Events for real-time push | ✅ |\n| 📋 OpenAPI Spec | Machine-readable 3.0.3 specification | ✅ |\n| 🚀 One-Click Deploy | Vercel button deployment | ✅ |\n| 🏗️ Express Mode | Standalone server with `npm start` | ✅ |\n\n---\n\n## 🗞️ News Sources\n\n| Source | Key | Method | Articles | Website |\n|:---|:---|:---|:---|:---|\n| **Anime News Network** | `ann` | Google News RSS | ~15 | [animenewsnetwork.com](https://www.animenewsnetwork.com/) |\n| **Anime Corner** | `animecorner` | RSS Feed | ~12 | [animecorner.me](https://animecorner.me/) |\n| **MyAnimeList** | `myanimelist` | Direct Scraping | ~15 | [myanimelist.net](https://myanimelist.net/) |\n| **Otaku USA Magazine** | `otakuusa` | Google News RSS | ~12 | [otakuusamagazine.com](https://otakuusamagazine.com/) |\n| **Crunchyroll** | `crunchyroll` | Google News RSS | ~15 | [crunchyroll.com/news](https://www.crunchyroll.com/news) |\n| **Anime Herald** | `animeherald` | RSS Feed | ~10 | [animeherald.com](https://www.animeherald.com/) |\n| **Comic Book** | `comicbook` | Direct Scraping | ~10 | [comicbook.com/anime](https://comicbook.com/anime/) |\n\n\u003e **Total: 60+ unique articles** after cross-source deduplication\n\n### Adding a New Source\n\n1. Create `utils/fetchNewSource.js` — export async function returning `[{ title, slug, source, excerpt, date, image, link, tags }]`\n2. Register in `utils/sources.js` → `SOURCES` object\n3. Test with `npm test`, submit a PR\n\n---\n\n## 🛠️ Tech Stack\n\n| Technology | Purpose | Version | Documentation |\n|:---|:---|:---|:---|\n| 🟢 [Node.js](https://nodejs.org/) | JavaScript runtime | \u003e= 20 | [Docs](https://nodejs.org/docs/) |\n| ⚡ [Express](https://expressjs.com/) | HTTP server framework | 5.1 | [Docs](https://expressjs.com/en/5x/api.html) |\n| ▲ [Vercel Functions](https://vercel.com/docs/functions) | Serverless deployment | — | [Docs](https://vercel.com/docs/functions) |\n| 🔍 [Cheerio](https://cheerio.js.org/) | HTML parsing \u0026 scraping | 1.0 | [Docs](https://cheerio.js.org/docs/) |\n| 🌐 [Axios](https://axios-http.com/) | HTTP client | 1.7 | [Docs](https://axios-http.com/docs/intro) |\n| 📡 [rss-parser](https://github.com/rbren/rss-parser) | RSS/Atom feed parsing | 3.13 | [Docs](https://github.com/rbren/rss-parser) |\n| 💾 [node-cache](https://github.com/ptarjan/node-cache) | In-memory caching | 5.1 | [Docs](https://github.com/ptarjan/node-cache) |\n| 🔤 [he](https://github.com/mathiasbynens/he) | HTML entity decoding | 1.2 | [Docs](https://github.com/mathiasbynens/he) |\n\n### 📦 Key Dependencies\n\n```json\n{\n  \"express\": \"^5.1.0\",        // HTTP server\n  \"axios\": \"^1.7.2\",          // HTTP client for scraping\n  \"cheerio\": \"^1.0.0-rc.12\",  // HTML parsing\n  \"rss-parser\": \"^3.13.0\",    // RSS feed parsing\n  \"node-cache\": \"^5.1.2\",     // In-memory cache\n  \"he\": \"^1.2.0\"              // HTML entity decoding\n}\n```\n\n---\n\n## 🏗️ Architecture\n\n### Request Flow\n\n| Stage | Component | Description |\n|:-----:|-----------|-------------|\n| 1 | **Client** | Browser, app, or `curl` sends request |\n| 2 | **Vercel Edge / Express** | Routes request, applies CORS + security headers + rate limit |\n| 3 | **Cache Check** | `node-cache` with 10-min TTL — hit = instant response |\n| 4 | **Fetch Sources** | 7 concurrent scrapers (3 retries each, 15s timeout) |\n| 5 | **Deduplicate** | Cross-source dedup by normalized title |\n| 6 | **Enrich \u0026 Respond** | Filter, paginate, sort, format → JSON/RSS/SSE |\n\n### Caching Architecture\n\n```mermaid\nflowchart TD\n    A[\"📥 Request\"] --\u003e B{\"🧠 Memory Cache\u003cbr/\u003e(node-cache)\"}\n    B -- HIT --\u003e C[\"⚡ Return Cached\u003cbr/\u003e~200ms\"]\n    B -- MISS --\u003e D{\"💾 Disk Cache\u003cbr/\u003e(JSON files)\"}\n    D -- HIT --\u003e E[\"🔄 Promote to Memory\u003cbr/\u003eReturn\"]\n    D -- MISS --\u003e F[\"📰 Fetch from 7 Sources\u003cbr/\u003e(concurrent)\"]\n    F --\u003e G[\"💾 Cache Result\u003cbr/\u003e(memory + disk)\"]\n    G --\u003e H[\"📤 Return Fresh\"]\n\n    style A fill:#1e1e2e,stroke:#a78bfa,color:#f1f5f9\n    style B fill:#1e1e2e,stroke:#f43f8e,color:#f1f5f9\n    style C fill:#1e1e2e,stroke:#22c55e,color:#f1f5f9\n    style D fill:#1e1e2e,stroke:#6366f1,color:#f1f5f9\n    style E fill:#1e1e2e,stroke:#06b6d4,color:#f1f5f9\n    style F fill:#1e1e2e,stroke:#eab308,color:#f1f5f9\n    style G fill:#1e1e2e,stroke:#a855f7,color:#f1f5f9\n    style H fill:#1e1e2e,stroke:#22c55e,color:#f1f5f9\n```\n\n\u003e 💡 Serverless functions have read-only filesystems except `/tmp`. The disk cache writes to `/tmp` on Vercel, surviving across warm invocations.\n\n### Source Fetch Strategy\n\n| Source | Primary | Fallback | Notes |\n|:---|:---|:---|:---|\n| ANN | Google News RSS | Direct scraping | Cloudflare blocks direct access |\n| Anime Corner | RSS Feed | Direct scraping | RSS has real descriptions |\n| MyAnimeList | Direct scraping | Page 2 scraping | Custom date format parser |\n| Otaku USA | Google News RSS | Direct scraping | 520 errors on direct access |\n| Crunchyroll | Google News RSS | Direct scraping | Blocks direct scraping |\n| Anime Herald | RSS Feed | Direct scraping | RSS has real descriptions |\n| Comic Book | Direct scraping | RSS Feed | Uses subheadline selector |\n\n---\n\n## 📁 Project Structure\n\n```\nAniNewsAPI/\n├── 📂 api/                            # 🌐 Vercel serverless functions\n│   ├── 📂 cache/\n│   │   └── 📄 clear.js                #    🔐 Cache management (API key protected)\n│   ├── 📄 health.js                   #    💚 Health check endpoint\n│   ├── 📄 news.js                     #    📰 Main news endpoint (pagination, filtering)\n│   ├── 📂 news/\n│   │   ├── 📄 [slug].js               #    📄 Full article by slug\n│   │   └── 📄 tags.js                 #    🏷️ Tag listing \u0026 filtering\n│   ├── 📄 openapi.js                  #    📋 OpenAPI 3.0.3 specification\n│   ├── 📄 rss.js                      #    🗞️ RSS 2.0 XML feed\n│   ├── 📄 search.js                   #    🔍 Full-text search with scoring\n│   ├── 📄 sources.js                  #    📊 Per-source health \u0026 stats\n│   ├── 📄 stats.js                    #    📈 Cache hit/miss statistics\n│   └── 📄 stream.js                   #    📡 Server-Sent Events\n│\n├── 📂 public/\n│   ├── 📄 index.html                  #    🏠 Landing page\n│   ├── 📄 manifest.json               #    📱 PWA manifest\n│   ├── 📄 og-image.png                #    🖼️ Open Graph image\n│   └── 📄 og-image.svg                #    🖼️ Open Graph vector\n│\n├── 📂 utils/                          # ⚙️ Core logic\n│   ├── 📄 cacheHandler.js             #    💾 Two-tier cache (memory + disk)\n│   ├── 📄 constants.js                #    📌 Shared config \u0026 defaults\n│   ├── 📄 contentParser.js            #    📄 Full-article content extraction\n│   ├── 📄 dateParser.js               #    📅 Multi-format date parsing\n│   ├── 📄 fetchANN.js                 #    📰 Anime News Network fetcher\n│   ├── 📄 fetchAnimeCorner.js         #    📰 Anime Corner fetcher\n│   ├── 📄 fetchAnimeHerald.js         #    📰 Anime Herald fetcher\n│   ├── 📄 fetchComicBook.js           #    📰 Comic Book fetcher\n│   ├── 📄 fetchCrunchyroll.js         #    📰 Crunchyroll fetcher\n│   ├── 📄 fetchMyAnimeList.js         #    📰 MyAnimeList fetcher\n│   ├── 📄 fetchOtakuNews.js           #    📰 Otaku USA fetcher\n│   ├── 📄 generateSlug.js             #    🔗 URL-safe slug generator\n│   └── 📄 sources.js                  #    📋 Centralized source registry\n│\n├── 📂 data/                           # 💾 Disk cache files (auto-generated)\n│\n├── 📄 server.js                       # 🚀 Express server entry point\n├── 📄 index.js                        # ▲ Vercel serverless entry point\n├── 📄 test.js                         # 🧪 Integration test suite\n├── 📄 vercel.json                     # ▲ Vercel routing \u0026 headers config\n├── 📄 package.json                    # 📦 Dependencies \u0026 scripts\n├── 📄 CHANGELOG.md                    # 📝 Version history\n├── 📄 CONTRIBUTING.md                 # 🤝 Contribution guidelines\n├── 📄 LICENSE                         # 📜 MIT License\n└── 📄 README.md                       # 📖 This file\n```\n\n---\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n| Requirement | Minimum | Recommended |\n|:---|:---|:---|\n| 📦 Node.js | 20.x | 20.x LTS |\n| 📦 npm | 9.0+ | 10.x |\n| 💻 OS | Windows, macOS, Linux | Any |\n\n### 🔧 Installation\n\n```bash\n# 1️⃣ Clone the repository\ngit clone https://github.com/Shineii86/AniNewsAPI.git\ncd AniNewsAPI\n\n# 2️⃣ Install dependencies\nnpm install\n\n# 3️⃣ Start development server\nnpm run dev\n```\n\n\u003e 🌐 Open [http://localhost:3000](http://localhost:3000) in your browser.\n\n### 🏗️ Build for Production\n\n```bash\n# Start production server\nnpm start\n\n# Run tests\nnpm test\n```\n\n### 🐳 Alternative Package Managers\n\n```bash\n# Using yarn\nyarn install\nyarn dev\n\n# Using pnpm\npnpm install\npnpm dev\n\n# Using bun\nbun install\nbun dev\n```\n\n---\n\n## ⚙️ Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|:---|:---|:---|\n| `CACHE_TTL` | `600` | Cache duration in seconds (10 minutes) |\n| `PORT` | `3000` | Server port (Express mode only) |\n| `CACHE_CLEAR_KEY` | — | API key for `POST /api/cache/clear` (optional) |\n| `API_URL` | `http://localhost:3000` | Base URL for test suite |\n\n### Vercel Configuration\n\nThe `vercel.json` file handles:\n- **Rewrites** — Maps clean URLs to serverless functions\n- **Headers** — CORS, caching, and rate limit headers\n- **Environment** — Sets `CACHE_TTL` for production\n\n---\n\n## 📡 API Endpoints\n\n### `GET /api/news`\n\nLatest anime news from all or specific sources.\n\n| Param | Type | Default | Description |\n|:---|:---|:---|:---|\n| `limit` | `1-100` | `20` | Max articles per page |\n| `offset` | `\u003e=0` | `0` | Pagination offset |\n| `cursor` | `string` | — | Pagination cursor (from `meta.nextCursor`) |\n| `sort` | `latest\\|oldest` | `latest` | Sort order |\n| `source` | `string` | `all` | Filter by source key |\n| `from` | `YYYY-MM-DD` | — | Start date filter |\n| `to` | `YYYY-MM-DD` | — | End date filter |\n| `refresh` | `boolean` | `false` | Bypass cache |\n\n```bash\n# Basic usage\ncurl \"https://aninews.vercel.app/api/news?limit=10\"\n\n# Filter by source with pagination\ncurl \"https://aninews.vercel.app/api/news?source=crunchyroll\u0026limit=10\u0026offset=10\"\n\n# Date range filtering\ncurl \"https://aninews.vercel.app/api/news?from=2026-05-20\u0026to=2026-05-27\"\n\n# Cursor-based pagination (use nextCursor from previous response)\ncurl \"https://aninews.vercel.app/api/news?limit=20\u0026cursor=eyJvZmZzZXQiOjIwfQ\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e📄 Example Response\u003c/summary\u003e\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"title\": \"Demon Slayer Season 4 Announced\",\n      \"slug\": \"ann-demon-slayer-season-4-announced\",\n      \"source\": \"Anime News Network\",\n      \"excerpt\": \"The official website confirmed...\",\n      \"date\": \"2026-05-27T10:30:00.000Z\",\n      \"image\": \"https://example.com/image.jpg\",\n      \"link\": \"https://www.animenewsnetwork.com/news/...\",\n      \"tags\": [\"news\", \"anime\"]\n    }\n  ],\n  \"meta\": {\n    \"total\": 62,\n    \"returned\": 10,\n    \"offset\": 0,\n    \"limit\": 10,\n    \"hasMore\": true,\n    \"nextCursor\": \"eyJvZmZzZXQiOjEwfQ\",\n    \"source\": \"all\",\n    \"sort\": \"latest\",\n    \"from\": \"2026-05-20\",\n    \"to\": \"2026-05-27\",\n    \"responseTime\": \"234ms\",\n    \"timestamp\": \"2026-05-27T12:00:00.000Z\"\n  }\n}\n```\n\u003c/details\u003e\n\n---\n\n### `GET /api/search`\n\nFull-text search with relevance scoring. Title matches rank higher than excerpt matches.\n\n| Param | Required | Description |\n|:---|:---|:---|\n| `q` | Yes | Search query (min 2 chars) |\n| `source` | No | Filter by source key |\n| `limit` | No | Max results (1-100) |\n| `offset` | No | Pagination offset |\n| `from` | No | Start date (YYYY-MM-DD) |\n| `to` | No | End date (YYYY-MM-DD) |\n\n**Scoring Algorithm:**\n- Title match: **+10 points** per search term\n- Excerpt match: **+3 points** per search term\n- Tiebreaker: newest date first\n\n```bash\ncurl \"https://aninews.vercel.app/api/search?q=demon+slayer\"\ncurl \"https://aninews.vercel.app/api/search?q=manga\u0026source=ann\u0026limit=5\"\ncurl \"https://aninews.vercel.app/api/search?q=crunchyroll\u0026from=2026-05-20\u0026to=2026-05-27\"\n```\n\n---\n\n### `GET /api/news/tags`\n\nList available tags with article counts, or filter articles by tag.\n\n```bash\n# List all tags with counts\ncurl \"https://aninews.vercel.app/api/news/tags\"\n\n# Filter articles by tag\ncurl \"https://aninews.vercel.app/api/news/tags?tag=official\"\n\n# Filter by tag and source\ncurl \"https://aninews.vercel.app/api/news/tags?tag=news\u0026source=ann\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e📄 Example Response (tag listing)\u003c/summary\u003e\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"tags\": [\n      { \"name\": \"anime\", \"count\": 45 },\n      { \"name\": \"news\", \"count\": 38 },\n      { \"name\": \"official\", \"count\": 22 }\n    ],\n    \"totalTags\": 15,\n    \"totalArticles\": 62\n  },\n  \"meta\": { \"timestamp\": \"2026-05-27T12:00:00.000Z\" }\n}\n```\n\u003c/details\u003e\n\n---\n\n### `GET /api/news/:slug`\n\nFull article content extraction from the original URL.\n\n```bash\ncurl \"https://aninews.vercel.app/api/news/ann-demon-slayer-season-4-announced\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e📄 Example Response\u003c/summary\u003e\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"title\": \"Demon Slayer Season 4 Announced\",\n    \"slug\": \"ann-demon-slayer-season-4-announced\",\n    \"source\": \"Anime News Network\",\n    \"excerpt\": \"The official website confirmed...\",\n    \"date\": \"2026-05-27T10:30:00.000Z\",\n    \"link\": \"https://www.animenewsnetwork.com/news/...\",\n    \"content\": \"\u003cp\u003eFull article HTML content...\u003c/p\u003e\",\n    \"author\": \"John Doe\",\n    \"publishDate\": \"2026-05-27\"\n  },\n  \"meta\": { \"cached\": false, \"timestamp\": \"2026-05-27T12:00:00.000Z\" }\n}\n```\n\u003c/details\u003e\n\n---\n\n### `GET /api/rss`\n\nStandards-compliant RSS 2.0 XML feed. Works with any feed reader.\n\n| Param | Default | Description |\n|:---|:---|:---|\n| `source` | `all` | Filter by source |\n| `limit` | `20` | Max items |\n\n```bash\ncurl \"https://aninews.vercel.app/api/rss\"\ncurl \"https://aninews.vercel.app/api/rss?source=crunchyroll\u0026limit=10\"\n```\n\n---\n\n### `GET /api/sources`\n\nPer-source health monitoring. Returns fetch status, article counts, and latency.\n\n```bash\ncurl \"https://aninews.vercel.app/api/sources\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e📄 Example Response\u003c/summary\u003e\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"key\": \"ann\",\n      \"name\": \"Anime News Network\",\n      \"status\": \"healthy\",\n      \"articleCount\": 15,\n      \"latency\": \"1234ms\",\n      \"lastFetch\": \"2026-05-27T12:00:00.000Z\",\n      \"lastError\": null\n    },\n    {\n      \"key\": \"crunchyroll\",\n      \"name\": \"Crunchyroll\",\n      \"status\": \"degraded\",\n      \"articleCount\": 0,\n      \"latency\": \"15001ms\",\n      \"lastFetch\": \"2026-05-27T11:55:00.000Z\",\n      \"lastError\": { \"message\": \"Timeout\", \"time\": \"2026-05-27T11:55:00.000Z\" }\n    }\n  ],\n  \"meta\": {\n    \"total\": 7,\n    \"healthy\": 6,\n    \"degraded\": 1,\n    \"responseTime\": \"2345ms\"\n  }\n}\n```\n\u003c/details\u003e\n\n---\n\n### `GET /api/health` · `GET /api/stats`\n\nHealth check and cache statistics.\n\n```bash\ncurl \"https://aninews.vercel.app/api/health\"\ncurl \"https://aninews.vercel.app/api/stats\"\n```\n\n---\n\n### `POST /api/cache/clear`\n\nManual cache flush. Requires API key when `CACHE_CLEAR_KEY` is set.\n\n```bash\n# Clear all caches\ncurl -X POST \"https://aninews.vercel.app/api/cache/clear\" \\\n  -H \"X-Api-Key: your-secret-key\"\n\n# Clear specific cache key\ncurl -X POST \"https://aninews.vercel.app/api/cache/clear\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Api-Key: your-secret-key\" \\\n  -d '{\"key\": \"news_all\"}'\n```\n\n---\n\n### `GET /api/stream`\n\nServer-Sent Events stream. Sends an initial burst of status data then closes.\n\n```bash\ncurl -N \"https://aninews.vercel.app/api/stream\"\n```\n\n\u003e ⚠️ Vercel Hobby functions timeout at 10s. This endpoint sends a single burst and closes. For real-time updates, poll `/api/news?refresh=true`.\n\n---\n\n### `GET /api/openapi`\n\nOpenAPI 3.0.3 specification in JSON format. Use with Swagger UI, Postman, or any OpenAPI-compatible tool.\n\n```bash\ncurl \"https://aninews.vercel.app/api/openapi\"\n```\n\n---\n\n## 📋 API Response Schema\n\n### Article Object\n\n| Field | Type | Description | Example |\n|:---|:---|:---|:---|\n| `title` | `string` | Article headline | `\"Demon Slayer Season 4\"` |\n| `slug` | `string` | URL-safe identifier | `\"ann-demon-slayer-season-4\"` |\n| `source` | `string` | Display name of source | `\"Anime News Network\"` |\n| `excerpt` | `string` | Article description/summary | `\"The official website...\"` |\n| `date` | `string` | ISO 8601 publish date | `\"2026-05-27T10:30:00.000Z\"` |\n| `image` | `string` | Thumbnail URL | `\"https://...\"` |\n| `link` | `string` | Original article URL | `\"https://...\"` |\n| `tags` | `string[]` | Category tags | `[\"news\", \"anime\"]` |\n\n### Pagination Meta\n\n| Field | Type | Description |\n|:---|:---|:---|\n| `total` | `number` | Total matching articles |\n| `returned` | `number` | Articles in this response |\n| `offset` | `number` | Current offset |\n| `limit` | `number` | Page size |\n| `hasMore` | `boolean` | Whether more pages exist |\n| `nextCursor` | `string\\|null` | Opaque cursor for next page |\n| `source` | `string` | Source filter applied |\n| `sort` | `string` | Sort order applied |\n| `responseTime` | `string` | Server processing time |\n\n---\n\n## 🌐 Deployment\n\n### ▲ Vercel (Recommended)\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Shineii86/AniNewsAPI)\n\n1. Click the button above (or import manually on vercel.com)\n2. Vercel auto-detects the project — **no config needed**\n3. Your API is live! 🎉\n\n```bash\n# Or use Vercel CLI\nnpx vercel --prod\n```\n\n### 🖥️ Standalone Server\n\n```bash\n# Clone and install\ngit clone https://github.com/Shineii86/AniNewsAPI.git\ncd AniNewsAPI \u0026\u0026 npm install\n\n# Start production server\nnpm start\n# → http://localhost:3000\n```\n\n### 🐳 Docker\n\n```dockerfile\nFROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --production\nCOPY . .\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n```\n\n---\n\n## 📜 Available Scripts\n\n| Command | Description | Details |\n|:---|:---|:---|\n| `npm run dev` | 🔥 Start development server | Runs on `localhost:3000` |\n| `npm start` | 🚀 Start production server | `NODE_ENV=production node server.js` |\n| `npm test` | 🧪 Run integration tests | Tests all 12 endpoints |\n| `npm run build` | 📦 Build (no-op for serverless) | Vercel handles this |\n\n---\n\n## ⚡ Performance\n\n| Metric | Value |\n|:---|:---|\n| ⚡ Cached response | ~200ms |\n| 🔄 Fresh fetch (all 7 sources) | ~3-6s |\n| 💾 Cache TTL | 10 minutes |\n| 🔁 Retry attempts | 3 per source |\n| ⏱️ Timeout per source | 15 seconds |\n| 📰 Total articles (avg) | 60+ after dedup |\n| 📦 Total codebase | ~50KB |\n\n### Optimization Features\n\n- 💾 **Two-tier cache** — Memory-first with disk fallback\n- ⚡ **Concurrent fetching** — All 7 sources hit simultaneously\n- 🔄 **Exponential backoff** — 1s, 2s, 3s delays on retry\n- 🧹 **Auto-cleanup** — Stale rate limit buckets purged every 5 min\n- 📁 **Disk persistence** — Source metrics survive serverless cold starts\n- 🗜️ **Minimal deps** — Only 6 production dependencies\n\n---\n\n## 📝 Changelog Highlights\n\n| Version | Date | Key Changes |\n|:---|:---|:---|\n| **4.2.0** | 2026-05-28 | Code style overhaul — AlisaReactionBot-style documentation across all 26 files |\n| **4.1.6** | 2026-05-27 | Full excerpts, no truncation — removed 200-char limit from all 7 fetchers |\n| **4.1.5** | 2026-05-27 | Real excerpts for Comic Book, Anime Corner, Anime Herald |\n| **4.1.4** | 2026-05-27 | Sources endpoint now does live health checks |\n| **4.1.3** | 2026-05-27 | Persist source metrics to disk for serverless survival |\n| **4.1.0** | 2026-05-26 | Date range filtering, cursor pagination, search scoring |\n\n\u003e 📝 See [CHANGELOG.md](./CHANGELOG.md) for the full version history.\n\n---\n\n## 🔧 Troubleshooting\n\n| Problem | Cause | Solution |\n|:---|:---|:---|\n| ❌ `npm install` fails | Node.js version too old | Upgrade to Node.js 20+ (`node -v`) |\n| ❌ No articles returned | All sources down | Check `/api/sources` for health status |\n| ❌ Cache always empty | Serverless cold start | Normal — first request after idle is slow |\n| ❌ Rate limited (429) | Exceeded 100 req/min | Wait for `X-RateLimit-Reset` seconds |\n| ❌ CORS errors | Frontend domain blocked | CORS is `*` — check browser extension |\n| ❌ RSS feed empty | No cached articles | Hit `/api/news` first to populate cache |\n| ❌ Article content empty | Source blocked parsing | Falls back to \"View original article\" link |\n| ❌ 404 on API routes | Wrong URL format | Use `/api/news` not `/news` |\n| ❌ Deploy fails on Vercel | Build error | Check `npm run build` locally first |\n| ❌ Tests failing | Server not running | Start server first with `npm run dev` |\n\n### 🐛 Debug Mode\n\n```bash\n# Run with verbose logging\nNODE_ENV=development npm run dev\n\n# Run tests against local server\nAPI_URL=http://localhost:3000 npm test\n\n# Check cache state\ncurl http://localhost:3000/api/stats\ncurl http://localhost:3000/api/health\n```\n\n---\n\n## ❓ FAQ\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📰 How do I add a new news source?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\n1. Create \u003ccode\u003eutils/fetchNewSource.js\u003c/code\u003e exporting an async function that returns an array of article objects: \u003ccode\u003e[{ title, slug, source, excerpt, date, image, link, tags }]\u003c/code\u003e\u003cbr/\u003e\n2. Register it in \u003ccode\u003eutils/sources.js\u003c/code\u003e — add an import and entry to the \u003ccode\u003eSOURCES\u003c/code\u003e object\u003cbr/\u003e\n3. Run \u003ccode\u003enpm test\u003c/code\u003e to verify, then submit a PR\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🔄 How often does the data refresh?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nThe cache TTL is 10 minutes by default. After that, the next request triggers a fresh fetch from all 7 sources. You can force a refresh with \u003ccode\u003e?refresh=true\u003c/code\u003e or change the TTL with the \u003ccode\u003eCACHE_TTL\u003c/code\u003e environment variable.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📡 Can I use this in my frontend app?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nYes! CORS is enabled for all origins (\u003ccode\u003e*\u003c/code\u003e). Just make fetch requests to the API endpoints. No API key needed for read operations. Example: \u003ccode\u003efetch('https://aninews.vercel.app/api/news?limit=10')\u003c/code\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🗞️ How do I subscribe to the RSS feed?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nAdd \u003ccode\u003ehttps://aninews.vercel.app/api/rss\u003c/code\u003e to any feed reader (Feedly, Inoreader, NetNewsWire, etc.). You can filter by source: \u003ccode\u003e/api/rss?source=crunchyroll\u003c/code\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📊 How does deduplication work?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nArticles are deduplicated by normalized title — punctuation is stripped, whitespace is collapsed, and compared case-insensitively. The first occurrence (from the source that responded fastest) wins.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🔒 Is the cache clear endpoint secure?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nWhen \u003ccode\u003eCACHE_CLEAR_KEY\u003c/code\u003e is set, the endpoint requires an \u003ccode\u003eX-Api-Key\u003c/code\u003e header. Without the env var, the endpoint is open — so set it in production. Read endpoints (\u003ccode\u003e/api/news\u003c/code\u003e, etc.) are always open.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e⏱️ Why is the first request slow?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nOn serverless (Vercel), the first request after idle triggers a \"cold start\" — the function initializes and fetches from all 7 sources (~3-6s). Subsequent requests hit the cache (~200ms). Warm functions stay alive for ~5 minutes.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🌐 Can I self-host this?\u003c/b\u003e\u003c/summary\u003e\n\u003cbr/\u003e\nYes! Use \u003ccode\u003enpm start\u003c/code\u003e to run the Express server on any VPS, Docker container, or PaaS. The Vercel serverless functions are optional — \u003ccode\u003eserver.js\u003c/code\u003e handles everything.\n\u003c/details\u003e\n\n---\n\n## 🗺️ Roadmap\n\n### 🎯 Planned Features\n\n- [ ] 🔐 **API key authentication** — Per-user rate limits and usage tracking\n- [ ] 📊 **Admin dashboard** — Web UI for cache management and source monitoring\n- [ ] 🌙 **Dark/light mode** — Theme toggle for the landing page\n- [ ] 📱 **PWA support** — Install as app on mobile devices\n- [ ] 🔔 **Webhook notifications** — Push new articles to Discord/Slack\n- [ ] 📈 **Analytics** — Track popular endpoints and search queries\n- [ ] 🗄️ **Database option** — Optional Supabase/Postgres for persistence\n- [ ] 🌐 **Multi-language** — Support for Japanese, Korean news sources\n- [ ] 🤖 **AI summaries** — Auto-generate article summaries\n- [ ] 📦 **NPM package** — Client SDK for easy integration\n\n### ✅ Completed\n\n- [x] 📰 7 news sources with concurrent fetching\n- [x] 💾 Two-tier caching (memory + disk)\n- [x] 🔍 Full-text search with relevance scoring\n- [x] 📅 Date range filtering\n- [x] 🔄 Cursor-based pagination\n- [x] 🗞️ RSS 2.0 feed\n- [x] 📄 Full article extraction\n- [x] 🏷️ Tag filtering with counts\n- [x] 📊 Source health monitoring\n- [x] 📋 OpenAPI 3.0.3 specification\n- [x] 📡 SSE stream\n- [x] 🔒 Cache clear authentication\n- [x] 🚀 One-click Vercel deployment\n- [x] 📖 Comprehensive documentation\n\n---\n\n## 🤝 Contributing\n\n*Contributions are welcome and appreciated! Here's how you can help:*\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"25%\" align=\"center\"\u003e\n\n### 🐛 Report Bugs\nFound something broken?\n\n[Open an Issue](https://github.com/Shineii86/AniNewsAPI/issues)\n\n\u003c/td\u003e\n\u003ctd width=\"25%\" align=\"center\"\u003e\n\n### 💡 Suggest Features\nHave an idea for the notebook?\n\n[Start a Discussion](https://github.com/Shineii86/AniNewsAPI/issues)\n\n\u003c/td\u003e\n\u003ctd width=\"25%\" align=\"center\"\u003e\n\n### 🔀 Submit PRs\nReady to contribute code?\n\n[Fork \u0026 Submit](https://github.com/Shineii86/AniNewsAPI/fork)\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n### 🔄 How to Contribute\n\n```bash\n# 1️⃣ Fork the repository\n# Click the \"Fork\" button on GitHub\n\n# 2️⃣ Clone your fork\ngit clone https://github.com/YOUR_USERNAME/AniNewsAPI.git\ncd AniNewsAPI\n\n# 3️⃣ Create a feature branch\ngit checkout -b feature/amazing-feature\n\n# 4️⃣ Make your changes\n# Edit files, add features, fix bugs...\n\n# 5️⃣ Test your changes\nnpm test\n\n# 6️⃣ Commit your changes\ngit commit -m 'feat: add amazing feature'\n\n# 7️⃣ Push to your fork\ngit push origin feature/amazing-feature\n\n# 8️⃣ Open a Pull Request\n# Go to GitHub and create a PR\n```\n\n### 📋 Guidelines\n\n- ✅ Follow the existing code style and documentation conventions\n- ✅ Write meaningful commit messages (use [conventional commits](https://www.conventionalcommits.org/))\n- ✅ Run `npm test` before submitting\n- ✅ Update CHANGELOG.md with your changes\n- ✅ Keep PRs focused — one feature or fix per PR\n- ✅ Add JSDoc comments for new functions\n- ❌ Don't commit `node_modules` or cache files\n- ❌ Don't add unrelated changes to a single PR\n\n### 🐛 Reporting Bugs\n\n1. Check existing [issues](https://github.com/Shineii86/AniNewsAPI/issues) first\n2. Create a new issue with:\n   - Clear title and description\n   - Steps to reproduce\n   - Expected vs actual behavior\n   - API endpoint and parameters used\n\n### 💡 Suggesting Features\n\n1. Check the [Roadmap](#-roadmap) for planned features\n2. Open a [feature request](https://github.com/Shineii86/AniNewsAPI/issues/new) with:\n   - Clear description of the feature\n   - Use case / motivation\n   - Example API usage if applicable\n\n---\n\n## 🙏 Acknowledgements\n\n### 📰 News Sources\n\n| Source | About |\n|:---|:---|\n| [Anime News Network](https://www.animenewsnetwork.com/) | Industry-leading anime journalism |\n| [Anime Corner](https://animecorner.me/) | Community-driven anime news \u0026 polls |\n| [MyAnimeList](https://myanimelist.net/) | The largest anime/manga database |\n| [Otaku USA Magazine](https://otakuusamagazine.com/) | English-language anime culture magazine |\n| [Crunchyroll](https://www.crunchyroll.com/news) | Official streaming platform news |\n| [Anime Herald](https://www.animeherald.com/) | Anime news, reviews \u0026 editorials |\n| [Comic Book](https://comicbook.com/anime/) | Anime \u0026 manga coverage at ComicBook |\n\n### 🛠️ Technologies\n\n- **[Express](https://expressjs.com/)** — Fast, unopinionated web framework\n- **[Cheerio](https://cheerio.js.org/)** — Fast, flexible HTML parsing\n- **[Axios](https://axios-http.com/)** — Promise-based HTTP client\n- **[rss-parser](https://github.com/rbren/rss-parser/)** — RSS/Atom feed parser\n- **[node-cache](https://github.com/ptarjan/node-cache/)** — In-memory caching\n- **[Vercel](https://vercel.com/)** — Serverless deployment platform\n\n### 📝 Resources\n\n- [Shields.io](https://shields.io/) — Badges for README\n- [Star History](https://star-history.com/) — GitHub star history charts\n\n---\n\n## 📄 License\n\n\u003cdiv align=\"center\"\u003e\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge\u0026logo=mit\u0026logoColor=white)](./LICENSE)\n\nThis project is licensed under the **MIT License**.\n\nFree to use, modify, and distribute — see the [LICENSE](LICENSE) file for details.\n\n\u003c/div\u003e\n\n---\n\n## 👤 Author\n\n\u003cdiv align=\"center\"\u003e\n\n  \u003ca href=\"https://github.com/Shineii86/AniNewsAPI\"\u003e\n  \u003cimg src=\"https://github.com/AniWaifuBot/Waifus/blob/main/Source/Banner.png\" alt=\"Banner\" width=\"100%\" /\u003e\n  \u003c/a\u003e\n  \n\u003c/div\u003e\n  \n\u003cp align=\"center\"\u003e\n  \u003cb style=\"font-size: 5.5em;\"\u003eShinei Nouzen\u003c/b\u003e\n  \u003cbr/\u003e\n  \u003csub\u003eFull-Stack Developer \u0026 Anime Enthusiast\u003c/sub\u003e\n  \u003cbr/\u003e\u003cbr/\u003e\n  \u003ca href=\"https://github.com/Shineii86\"\u003e\u003cimg src=\"https://img.shields.io/badge/GitHub-100000?style=for-the-badge\u0026logo=github\u0026logoColor=white\" alt=\"GitHub\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://telegram.me/Shineii86\"\u003e\u003cimg src=\"https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge\u0026logo=telegram\u0026logoColor=white\" alt=\"Telegram\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://instagram.com/ikx7.a\"\u003e\u003cimg src=\"https://img.shields.io/badge/Instagram-C13584?style=for-the-badge\u0026logo=instagram\u0026logoColor=white\" alt=\"Instagram\"/\u003e\u003c/a\u003e\n  \u003ca href=\"mailto:ikx7a@hotmail.com\"\u003e\u003cimg src=\"https://img.shields.io/badge/Email-D14836?style=for-the-badge\u0026logo=gmail\u0026logoColor=white\" alt=\"Email\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## ⭐ Star History\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://star-history.com/#Shineii86/AniNewsAPI\u0026Date\"\u003e\n    \u003cimg src=\"https://api.star-history.com/svg?repos=Shineii86/AniNewsAPI\u0026type=Date\" alt=\"Star History Chart\" width=\"100%\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003e ⭐ If you found this project useful, please consider giving it a star!\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eMade With ❤️ For The Anime Community\u003c/b\u003e\n  \u003cbr/\u003e\u003cbr/\u003e\n  \u003csub\u003e© 2026 Shineii86. All Rights Reserved.\u003c/sub\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshineii86%2Faninewsapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshineii86%2Faninewsapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshineii86%2Faninewsapi/lists"}