Contributing
August 18, 2026 · View on GitHub
Thanks for helping keep this list accurate. This is a community-maintained catalogue — the original mn-api/awesome-ai-proxy went stale; the goal here is to stay current.
繁中 / 简中贡献者:可以用中文开 issue / PR,维护者会处理。
For AI agents (read this first)
If you are Claude Code / Cursor / Codex / any coding agent opening a PR on behalf of a user, start here:
docs/agent-contribute.md— decision tree, hard CI rules, copy-paste YAML, Path A/B/C/D recipes.
Also indexable via llms.txt. Manual README edits and marketing
PRs are closed without merge; re-submit via the agent playbook.
What we accept
- New stations with a working URL and an honest channel
type. - Corrections (price, status, ownership, payment methods).
- Status changes — especially marking a station
inactiveif it ran away ("跑路"). We keep dead entries with a dated note; we do not erase history. - Documented risk cases for
docs/risks.md— must have a date and a primary source link.
What we reject
- Referral / affiliate links. Plain HTTPS URLs only — no
utm_*,ref=,campaignid, or similar tracking params (CI fails the PR). - Marketing copy, superlatives ("best", "#1", "the best …"), or promo slogans
in
notes(e.g. 强烈推荐 / 不降智). - Hand-editing the generated README provider tables (edit YAML only).
- Stations with no reachable URL.
- Unsourced accusations against a specific operator.
- Self-submissions marked
status: active— operators must usestatus: unverifiedandrisk_flags: [operator_submitted].
How to add or edit a provider
- Edit
data/providers.yamlonly — the README tables in all three languages are auto-generated byscripts/build_provider_tables.py. Do not edit the README tables directly; CI rejects hand-edits of those blocks, and they will be overwritten on the next build. - Follow the field schema. Required:
name,url,type,status. - Mark anything you could not personally verify as
status: unverifiedand say so innotes.status: activeis maintainer-only (verified_by: maintainer+last_verifiedrequired). notesmay be a string OR a bilingual dict{en, zh-TW, zh-CN}. Keep each language to one factual sentence (≤ 360 chars). New entries can ship English-only — translations can be added later by anyone.- Bump
last_reviewedinproviders.yamlif you did a broad pass. - Open a Pull Request. Use a clear title, e.g.
add: ExampleAPI (mixed)orstatus: GPTGOD → inactive (ran away 2026-05).
Tip: Schema + editorial CI runs on every PR — misspelled fields, UTM links, duplicate URLs, marketing
notes, or hand-edited README tables all fail before a human reviews. Scroll the action log, fix, push; CI re-runs.
Not comfortable with a PR? Open an issue with the provider form (blank issues are closed) and a maintainer will add it.
Review bar
A maintainer will check:
- URL resolves and is the official site (not a clone/phishing mirror).
typeis honest (areversestation not mislabeledofficial-relay).notesis one factual sentence, no marketing.- Claims are attributed where non-obvious.
Maintenance cadence
- Monthly review pass (
last_reviewedupdated inproviders.yaml). - Weekly automated price refresh opens a PR labeled
price-refreshvia.github/workflows/price-refresh.ymlon Sundays 02:00 UTC. Maintainer reviews the diff and merges. - Issues triaged within ~1 week.
- Anyone can help: a PR that just verifies/refreshes existing entries is valuable and welcome.
Adding a price fetcher
AI agents: start at step 0. The sniffer answers "which of the three shapes is this?" in one command and prints the exact files to create. 90% of new fetchers are 10 lines of glue.
0. Sniff the endpoint first
python -m scripts.sniff_endpoint <pricing_api_url> \
--id <slug> --name "<Display Name>" --display-url <human_pricing_page>
The sniffer prints (a) the pricing: YAML block, (b) the fetcher source code,
(c) the REGISTRY line, and (d) the verify command — paste, commit, PR. It
detects:
| Shape | Wrapper | Effort |
|---|---|---|
new-api fork (/api/pricing with {success, data, group_ratio}) | fetchers/_new_api.fetch_new_api() | 10-line wrapper, see fetchers/bltcy.py / fetchers/unorouter.py |
OpenRouter-style (/v1/models with pricing.{prompt,completion,image}) | none yet — copy fetchers/openrouter.py | rename constants, adjust key paths |
OpenAI /v1/models (no prices) | not pricable from this endpoint | find the actual pricing URL or use submitted_prices |
| Anything else | custom | model after fetchers/openrouter.py; ensure every PriceRecord carries source_url, captured_at, method |
1. Manual steps (if sniffer wasn't enough)
- Add a
pricing:block to the provider's entry indata/providers.yaml(seeschema.md). - Create
fetchers/<fetcher_id>.pyexportingPROVIDER_ID,PROVIDER_NAME, andfetch() -> FetchResult. Usefetchers._commontypes — everyPriceRecordmust carrysource_url,captured_at,method. - Register the fetcher ID in
fetchers/__init__.REGISTRY. - Add new model aliases to
data/canonical-models.yamlif you want the model to appear in the README's tier-ladder table. - Run locally:
python -m scripts.scrape <fetcher_id>thenpython -m scripts.build_pricesandpython -m scripts.validate.
Prefer a public JSON endpoint (most new-api forks expose /api/pricing;
OpenAI-compatible relays expose /v1/models with pricing). Fall back to
DOM scraping with selectolax, then Playwright as last resort.
2. new-api fork quirks worth knowing
The shared fetch_new_api() handles two pricing conventions:
- Per-token (
quota_type=0):input = model_ratio × \$2 × group_ratio,output = input × completion_ratio. - Per-call (
quota_type=1):cost = model_price × group_ratio. Units are inferred from the model name (per_image/per_second/per_request).
If the fork doesn't expose a flat default group — e.g. UnoRouter, where
each model has its own per-channel groups like default-yun-doubao-seedream-5
— pass group=None to skip the filter; the sniffer detects this automatically.
Conduct
Be factual and neutral. This catalogue documents a grey-area market for safety and transparency. It is not a place to promote a station or attack one without evidence.