freeapi.pages.dev — deployed on Cloudflare Pages with Supabase authentication (GitHub OAuth + Email OTP).
A static, front-end directory of 240+ AI API sites — LinuxDo community free stations, free chat mirrors, free/paid API relays, overseas official free tiers, domestic LLM platforms, plus an open-source framework index and a blacklist of dead endpoints. Live availability probing and zh / en / ja UI, with zero backend by default.
FreeAPI collects the fragmented landscape of "free / cheap" LLM access points into a single filterable surface. Each entry is tagged by category, type (free / freemium / paid), status, supported models, and a short feature list (free, daily check-in, free quota, domestic, low-latency, Linux.do gated, etc.). Availability is probed from the browser with a cached image beacon, and an optional Cloudflare Worker upgrades probing to real /v1/models endpoint checks that bypass CORS.
The project is opinionated about not pretending: domain reachability is not API availability, and that distinction is surfaced in the UI rather than hidden.
-
240+ curated entries across 8 categories — counts as of the last data refresh:
Category Slug Count Coverage Paid relay paidrelay87 Commercial resellers of GPT / Claude at lower unit price Free chat freechat81 Mirror / aggregator chat sites, no key required Blacklist blacklist21 Confirmed dead (domain resold, SSL expired, service shut down) Community free linuxdo15 LinuxDo community-run free / welfare API stations Overseas free tier overseas11 OpenRouter / Groq / Gemini official free tiers Frameworks & nav tool10 Open-source relay frameworks, benchmarks, index sites Domestic official domestic9 PRC LLM vendor official API platforms Free relay freerelay8 API forwarders offering a free quota Total 242 -
Live availability probing — front-end
imgbeacon against/favicon.icowith a 1-hourlocalStoragecache; optional Cloudflare Worker hits/v1/modelsand treats401/403as "API online but key required". -
Trilingual UI —
zh/en/ja, preference persisted tolocalStorage(FreeAPI-lang); site data (name / desc / tagline) stays in its source language, only framework strings are translated. -
Mobile-first — responsive grid (2 cols on phone → 8 cols on wide screens), compact spacing, stacked toolbar.
-
Cyberpunk terminal aesthetic — fixed dark palette, monospaced data, scan-line animation.
-
No backend dependency by default — all site data is compiled into the bundle; deployable to any static host.
| Token | Hex | Use |
|---|---|---|
cyber.bg |
#0a0e14 |
Page background |
cyber.cyan |
#00e5ff |
Primary accents, "online" |
cyber.magenta |
#ff2e88 |
Free-chat category |
cyber.amber |
#ffb020 |
Paid relay, "unstable" |
cyber.green |
#34d399 |
"OK" status |
cyber.violet |
#7c5cff |
Free relay |
cyber.blue |
#60a5fa |
Domestic official |
cyber.dead |
#ef4444 |
Blacklist / "dead" |
Typography: Chakra Petch (display) + JetBrains Mono (monospaced data) + Noto Sans SC (body). The muted token was raised to #8b95a8 to clear WCAG AA contrast (≥ 4.6:1) against the background.
| Layer | Choice |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite 6 |
| Styling | Tailwind CSS 3 (dark-mode class) |
| State | Zustand 5 (persist-free; filter state is in-memory) |
| Auth | Supabase (GitHub OAuth + Email OTP via Resend) |
| Routing | react-router-dom 7 via hash routing (#/blacklist) |
| Icons | lucide-react |
| Probing | Front-end img beacon + optional Cloudflare Worker |
flowchart LR
subgraph Bundle["Static bundle (dist/)"]
A[React UI] --> S[sites.ts<br/>242 entries]
A --> F[useFilterStore<br/>Zustand]
A --> H[useSiteHealth]
end
H -->|img beacon /favicon.ico| B[(Browser localStorage<br/>1h TTL cache)]
H -.->|optional, when HEALTH_API set| W[(Cloudflare Worker<br/>/v1/models probe)]
W --> C[(Cache API<br/>5 min)]
A --> R{hash route}
R -->|#/blacklist| BL[Blacklist page]
R -->|default| HM[Home page]
# install (pnpm recommended; npm works too)
pnpm install
# dev server at http://localhost:5173
pnpm dev
# type-check + production build → dist/
pnpm check && pnpm build
# lint
pnpm lintRequires Node 18+ and a recent pnpm.
Environment variables (create a .env file — never commit it):
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-keyWithout these, login is disabled and the site works as a read-only directory.
freeapi/
├── src/
│ ├── components/
│ │ ├── Hero.tsx # header, category stat cards, live badge
│ │ ├── Toolbar.tsx # search / model / sort / type / status filters
│ │ ├── SiteGrid.tsx # grouped + flat grid rendering
│ │ ├── SiteCard.tsx # single entry card + feature tags
│ │ ├── DetailDrawer.tsx # per-site detail drawer
│ │ ├── AuthModal.tsx # GitHub OAuth + Email OTP login modal
│ │ ├── UserMenu.tsx # user dropdown with profile link & logout
│ │ ├── LangSwitcher.tsx # zh / en / ja switcher
│ │ └── Empty.tsx
│ ├── data/sites.ts # canonical site list + category/type/status meta
│ │ # + deriveFeatures() auto-tagging
│ ├── i18n/
│ │ ├── translations.ts # zh / en / ja dictionary + format()
│ │ └── useI18n.ts # zustand store + useT() + translateFeature()
│ ├── store/
│ │ ├── useFilterStore.ts # filter state + derived visible sites + live status
│ │ ├── useAuthStore.ts # Supabase session + user + OTP timer
│ │ └── useFavoritesStore.ts# favorites (site IDs) persisted to localStorage
│ ├── hooks/
│ │ ├── useSiteHealth.ts # beacon probing + batched recheck
│ │ └── useTheme.ts
│ ├── pages/
│ │ ├── Home.tsx
│ │ ├── Blacklist.tsx # #/blacklist
│ │ └── Profile.tsx # #/me — saved favorites, user info
│ ├── App.tsx # hash router
│ └── main.tsx
├── api/ # optional Cloudflare Worker probe backend
│ ├── health.worker.js
│ ├── sites.json # generated by scripts/export-sites.mjs
│ └── wrangler.toml
├── scripts/ # site export / verification scripts
└── public/
All entries live in src/data/sites.ts:
interface Site {
id: string;
name: string;
url: string;
category: 'linuxdo' | 'freechat' | 'freerelay' | 'paidrelay'
| 'overseas' | 'domestic' | 'tool' | 'blacklist';
type: 'free' | 'freemium' | 'paid';
status: 'ok' | 'unstable' | 'unknown' | 'dead';
models: string[];
desc: string;
tagline?: string; // one-line positioning under the name
features?: string[]; // explicit tags; auto-derived if absent
apiBase?: string;
billing?: string;
register?: string;
payment?: string;
note?: string;
blacklistReason?: string; // only for category === 'blacklist'
}When features is omitted, deriveFeatures() infers tags from type / billing / note / register / desc (e.g. type === 'free' → 免费, mention of "签到" → 签到). Tag → colour mapping lives in FEATURE_COLOR inside SiteCard.tsx and DetailDrawer.tsx.
- Three locales:
zh/en/ja, dictionary insrc/i18n/translations.ts. useT()for component strings;translate()for non-hook contexts.- Site data (name / desc / tagline) is kept in its source language — only framework UI is translated.
- Feature tags translate via
translateFeature()keyed asfeat.<source-label>. - Language choice persists to
localStorageunderFreeAPI-lang; missing keys fall back to Chinese.
The front-end probe is deliberately honest about its limits: an img beacon against /favicon.ico can only confirm that the domain responds, not that the API serves. Relay home pages almost always return 200, so "domain up" ≠ "API usable". The UI labels this distinction rather than masking it.
sequenceDiagram
participant U as User opens app
participant F as useSiteHealth
participant LS as localStorage cache
participant Net as Network (img beacon)
U->>F: mount
F->>LS: read FreeAPI:live-status
alt cache fresh (< 1h)
F-->>U: render cached status instantly
else stale
F->>Net: batched probe (6 sites / 400ms gap)
Net-->>F: onload/onerror → up | timeout → unknown
F->>LS: write updated entries
F-->>U: progressive UI update + progress bar
end
For accurate probing, deploy the optional Cloudflare Worker — it calls /v1/models without a key and interprets 401/403 as "API online" (key required), 5xx/timeout as "down". The front-end auto-switches to Worker mode when HEALTH_API is set in src/hooks/useSiteHealth.ts, and falls back to the beacon if the Worker is unreachable.
# 1. export the site manifest from sites.ts
node scripts/export-sites.mjs # → api/sites.json
# 2. deploy the worker
cd api && npx wrangler deploy
# 3. fill the workers.dev URL into src/hooks/useSiteHealth.ts → HEALTH_APISee api/README.md for the full deploy guide, cost estimate, and troubleshooting matrix.
| Endpoint | Method | Returns |
|---|---|---|
/ |
GET | {"ok":true,"sites":N,"version":"1.0.0"} |
/api/sites |
GET | {total, sites:[{id,url,apiBase,category}]} |
/api/health?id=X |
GET | {id,status,latency,checkedAt,detail} |
/api/health?batch=all |
GET | {total,results:[...],checkedAt} |
/api/health?batch=linuxdo |
GET | same shape, scoped to a category |
Status semantics: up = 2xx/3xx/4xx (4xx means the API is online but a key is required); down = 5xx or network error; unknown = probe fault. Cache TTL is 300 s (Cache API); per-IP rate limit is 60 req/min (429 on excess).
FreeAPI supports optional login via Supabase:
- GitHub OAuth — one-click sign-in with your GitHub account
- Email OTP — enter your email, receive a 6-digit code, no password needed (powered by Resend)
Once logged in, you can favorite sites (star icon on each card) and view them later on the Profile page (#/me). Favorites are persisted to localStorage per account.
Auth state is managed in useAuthStore (Zustand) and session is synced with Supabase in real-time. The UI adapts: unauthenticated users see a "Login" button; logged-in users see their avatar with a dropdown to navigate to Profile or sign out.
The front-end is deployed to Cloudflare Pages. Cloudflare Pages connects directly to the GitHub repository and auto-builds on every push to main:
- Cloudflare detects the push and runs
pnpm install+pnpm build VITE_SUPABASE_URLandVITE_SUPABASE_ANON_KEYare set as environment variables in the Cloudflare Pages dashboard- The built
dist/is deployed tohttps://freeapi.pages.dev/
GitHub Actions (.github/workflows/deploy.yml) runs CI checks (lint + build) on every push and pull request to ensure the build passes before Cloudflare deploys.
The site is served from https://freeapi.pages.dev/. public/_redirects provides SPA fallback (/* → /index.html 200) and public/_headers adds security headers + asset caching.
For self-hosting: pnpm build produces dist/, uploadable to any static host (Vercel, Netlify, GitHub Pages).
Site additions, status corrections, and translation fixes are all welcome. Add entries to src/data/sites.ts following the schema above; fill tagline and features where possible since deriveFeatures() only covers the common cases. When adding a new feature tag, also extend feat.<label> in all three languages and update FEATURE_COLOR in both SiteCard.tsx and DetailDrawer.tsx. See CONTRIBUTING.md for the full workflow and submission norms.
The project does not list sites that violate applicable law or ship malware / phishing / crypto-mining payloads. Dead sites are moved to blacklist with a recorded reason rather than silently deleted.
MIT © 2026 weed