weed33834.github.io/FreeAPI — deployed on GitHub Pages with Supabase authentication (Email/Password).
A static, front-end directory of 278 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, deep API health checks, side-by-side site comparison, a built-in guide, 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 fetch-based check.
The project is opinionated about not pretending: domain reachability is not API availability, and that distinction is surfaced in the UI rather than hidden.
-
278 curated entries across 8 categories — counts as of the last data refresh:
Category Slug Count Coverage Paid relay paidrelay104 Commercial resellers of GPT / Claude at lower unit price Free chat freechat83 Mirror / aggregator chat sites, no key required Overseas free tier overseas23 OpenRouter / Groq / Gemini / Cloudflare / Cohere official free tiers Community free linuxdo21 LinuxDo community-run free / welfare API stations Blacklist blacklist14 Confirmed dead (domain resold, SSL expired, service shut down) Frameworks & nav tool12 Open-source relay frameworks, benchmarks, index sites Domestic official domestic11 PRC LLM vendor official API platforms Free relay freerelay10 API forwarders offering a free quota Total 278 -
Live availability probing — front-end
fetchagainst API endpoints and site homepages with a 1-hourlocalStoragecache. Probing confirms domain reachability; see the "Live health check" section for an honest explanation of its limits. -
Deep health check — on demand,
useDeepHealthCheckgoes beyond domain reachability and actually calls the API:HEAD {apiBase}/models, falling back to a minimalPOST {apiBase}/chat/completions, reporting the real HTTP status code and latency so you can tell whether the API truly serves. -
Site comparison — pin up to 4 sites (
useCompareStore) and openCompareModalto contrast models, pricing, status, and features side by side. -
Shareable filter URLs —
useUrlFilterstwo-way syncs the toolbar state to the URL hash (#/?cat=linuxdo&type=free&kw=gpt&model=claude&status=ok&api=1), debounced 300ms, so any view is copy-paste shareable and bookmarkable. -
Keyboard shortcuts —
/focuses the search box,Escclears all filters (home view only; suppressed while typing in inputs). -
Toast notifications — a global
ToastContainerbacked byuseToastStoresurfaces copy / auth / report feedback without modal interruptions. -
Theme toggle — default cyberpunk dark theme with an optional light theme (
useThemeStore, persisted tolocalStorageunderFreeAPI-theme). -
Built-in guide —
#/guiderenders a 5-section collapsible guide withcurl/ Python / Node examples for wiring a relay site into OpenAI-compatible clients. -
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 — dark-first palette, monospaced data, scan-line animation, with a light theme option.
-
Installable PWA —
public/manifest.json(standalone display, themed, maskable icon) lets you add FreeAPI to your home screen. -
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 19.2 + TypeScript 7.0 |
| Build | Vite 8 |
| Styling | Tailwind CSS 4 (dark-mode class, light theme toggle) |
| State | Zustand 5 (theme + favorites persisted; filter state in-memory) |
| Auth | Supabase (Email/Password) |
| Routing | custom hash routing (#/blacklist, #/me, #/guide) |
| Icons | lucide-react |
| Probing | Front-end fetch (HEAD) + deep POST /chat/completions check |
| PWA | Web App Manifest (public/manifest.json) |
flowchart LR
subgraph Bundle["Static bundle (dist/)"]
A[React UI] --> S[sites.ts<br/>278 entries]
A --> F[useFilterStore<br/>Zustand]
A --> UF[useUrlFilters<br/>hash ↔ filters]
A --> H[useSiteHealth]
A --> DC[useDeepHealthCheck]
A --> CMP[useCompareStore]
A --> TH[useThemeStore]
A --> T[useToastStore]
end
H -->|fetch HEAD /models or /| B[(Browser localStorage<br/>1h TTL cache)]
DC -->|HEAD /models + POST /chat/completions| N[(Network)]
A -->|auth + favorites| SB[(Supabase<br/>auth + DB)]
A --> R{hash route}
R -->|#/blacklist| BL[Blacklist page]
R -->|#/me| PF[Profile page]
R -->|#/guide| GD[Guide 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 20+ 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 + deep health check
│ │ ├── CompareModal.tsx # side-by-side comparison of up to 4 sites
│ │ ├── ToastContainer.tsx # global toast notification stack
│ │ ├── AuthModal.tsx # Email/Password register & login modal
│ │ ├── PasswordInput.tsx # password field with show/hide toggle
│ │ ├── UserMenu.tsx # user dropdown with profile link & logout
│ │ ├── LangSwitcher.tsx # zh / en / ja switcher
│ │ ├── ReportModal.tsx # site issue reporting modal
│ │ └── ErrorBoundary.tsx # top-level error fallback
│ ├── 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()
│ ├── lib/
│ │ ├── supabase.ts # Supabase client + AUTH_ENABLED flag
│ │ ├── auth-utils.ts # Email validation + error mapping
│ │ ├── constants.ts # FEATURE_COLOR, CAT_ORDER, LIVE_COLOR
│ │ ├── date-utils.ts # relative-time formatting helpers
│ │ └── utils.ts # cn() + copyToClipboard
│ ├── store/
│ │ ├── useFilterStore.ts # filter state + derived visible sites + live status
│ │ ├── useAuthStore.ts # Supabase session + user
│ │ ├── useFavoritesStore.ts# favorites with DB sync + localStorage
│ │ ├── useCompareStore.ts # compare tray (max 4) + modal open state
│ │ └── useToastStore.ts # toast queue + push / dismiss
│ ├── hooks/
│ │ ├── useSiteHealth.ts # batched reachability probing + useDeepHealthCheck
│ │ ├── useUrlFilters.ts # two-way sync of filters ↔ URL hash
│ │ ├── useKeyboard.ts # `/` focus search, `Esc` clear filters
│ │ ├── useTheme.ts # dark/light theme store + <html> class
│ │ └── useUserProfile.ts # profile data fetch + cache
│ ├── pages/
│ │ ├── Home.tsx
│ │ ├── Blacklist.tsx # #/blacklist
│ │ ├── Profile.tsx # #/me — saved favorites, user info
│ │ └── Guide.tsx # #/guide — usage guide with code examples
│ ├── App.tsx # hash router (home / blacklist / me / guide / 404)
│ └── main.tsx
├── scripts/ # site export / verification scripts
└── public/
├── manifest.json # PWA manifest (standalone, themed, maskable icon)
├── favicon.svg
├── robots.txt
└── sitemap.xml
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;
pros?: string[]; // site advantages (shown in detail drawer)
cons?: string[]; // site disadvantages (shown in detail drawer)
tips?: string; // usage tips (shown in detail drawer)
blacklistReason?: string; // only for category === 'blacklist'
blacklistReasonType?: 'domain-sale' | 'http-error' | 'not-found'
| 'repo-removed' | 'service-stopped'
| 'ssl-error' | 'timeout' | 'other';
}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 src/lib/constants.ts.
- 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: a fetch HEAD against API endpoints or site homepages 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.
For when reachability is not enough, the per-site detail drawer offers a deep health check (useDeepHealthCheck): it issues HEAD {apiBase}/models and, if that is inconclusive, a minimal POST {apiBase}/chat/completions, then surfaces the real HTTP status code and latency. This is the closest a zero-backend, browser-only probe can get to verifying that an API actually serves — bounded, as ever, by CORS (no-cors fallbacks return unknown rather than a false "up").
sequenceDiagram
participant U as User opens app
participant F as useSiteHealth
participant LS as localStorage cache
participant Net as Network (fetch HEAD)
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 (8 sites / 300ms gap)
Net-->>F: HTTP status → up | timeout → down
F->>LS: write updated entries
F-->>U: progressive UI update + progress bar
end
FreeAPI supports optional login via Supabase:
- Email/Password — register with email + password + username, no email verification required
- Password reset — forgot password? send a reset link to email, update password on return
Once logged in, you can favorite sites (heart icon on each card) and view them later on the Profile page (#/me). Favorites sync to the database (cross-device) with localStorage as offline cache.
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 GitHub Pages via GitHub Actions (.github/workflows/deploy.yml). On every push to main, the workflow:
- Checks out the repo and installs pnpm dependencies
- Injects
VITE_SUPABASE_URLandVITE_SUPABASE_ANON_KEYfrom GitHub Secrets - Builds the production bundle with
pnpm build - Uploads and deploys the
dist/artifact to GitHub Pages
The site is served from https://weed33834.github.io/FreeAPI/. Hash routing (#/blacklist, #/me) requires no server-side rewrite rules.
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 src/lib/constants.ts. 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