chenyu-li.info / api / deployment
Deployment Decision

Deploy the APIs as separate versioned services.

Once the flagship is the Security Selection Data API, the infrastructure decision becomes clearer: you are not deploying one toy endpoint. You are deploying a small portfolio of Python API services with different latency and compute profiles.

Recommendation: keep the website and docs on Firebase Hosting, and deploy each paid API family as its own Cloud Run service from the remote machine. Route them behind one public API domain.

Recommended public topology

One domain, multiple services, static docs kept separate from runtime.

Website

chenyu-li.info/api/... on Firebase Hosting for docs, pricing, and product copy.

API Gateway Domain

api.<domain> path-routed to different backend services.

Runtime Services

Separate Cloud Run services for `data`, `meta-thinking`, and `sim`.

Public path Service Source Why separate it
/data/* Security Selection Data API scripts/data_api/serve_data.py Main data product, broad route surface, likely highest traffic
/meta-thinking/* Meta-Thinking API scripts/meta_thinking/serve.py Async job compute profile, different concurrency needs
/sim/* Corporate Simulation API corporate_sim/serve_sim.py Heavier runs and streaming endpoints justify separate runtime control
Dedicated subdomain or private ingress Institutional custom deployment Client-specific branch, config, and data connectors Lets enterprise clients buy a tailored system without forcing their requirements into the shared public product

Anti-scraping and abuse control

You cannot completely stop a paying customer from exporting data, but you can make anonymous scraping, key sharing, and abusive automation much harder and much more expensive.

Layer Control What it does
Ingress Global HTTPS load balancer in front of Cloud Run Gives one choke point for WAF, rate limiting, and bot controls
Edge policy Cloud Armor `throttle` for normal abuse and `rate_based_ban` for repeated offenders Limits burst scraping and temporarily bans clients that blow through thresholds
Bot friction Cloud Armor bot management and reCAPTCHA on signup, docs scraping hotspots, and suspicious interactive paths Stops cheap automated scraping against public-facing surfaces without putting captchas on every API request
API identity Scoped API keys, per-key monthly unit quota, per-key burst cap, and endpoint-family entitlements Separates who can call what from how much they can call
Paid plans IP allowlists for Growth and mandatory allowlists plus signed requests for Scale Reduces key sharing and makes account-level abuse attributable
Backend meter Usage-unit ledger in Postgres or Redis-backed counters Lets you enforce overage billing, hard caps, and anomaly detection per customer
Route hardening Disable the default Cloud Run URL Prevents attackers from bypassing the load balancer and Cloud Armor controls

What to meter

Watch for per-key request bursts, too many unique symbols per minute, highly sequential CIK or ticker walks, repeated query fan-out, and key usage from too many IPs. Those are scraper signatures, not normal analyst usage.

Operational rule

Public docs stay open. The paid runtime sits behind key auth, quotas, rate limits, and load-balancer policy. Do not expose long-lived production keys in browser JavaScript.

Enterprise variation

Institutional clients can run on a dedicated service, private ingress, or a client-specific environment. That is the right place for custom workflows, proprietary connectors, and stricter access controls.

Provider comparison

The winning criterion is versioned release control for multiple Python APIs, not simply how easy it is to launch one demo app.

Provider Strong fit here Weak fit here Verdict
Cloud Run Container-native, revisions, traffic splitting, rollback, strong fit for several FastAPI services Custom domains should use the recommended load-balancer path, not the preview mapping path Best default
Fly.io Good for public demos, Anycast networking, ergonomic global deploys Not my first choice once product state and controlled multi-service releases matter Good for demos, not the primary revenue path
Cloudflare Workers Great edge gateway and request economics Less natural as the primary home for container-heavy Python services and job-style APIs Useful future gateway option, not the core runtime
Railway Friendly workflow, staged changes, simpler UX than raw cloud tooling Weaker exact-release story than Cloud Run revisions for this use case Reasonable fallback
Render Predictable pricing, zero-downtime deploys, custom domains Less compelling than Cloud Run for image-tagged release control across several services Reasonable fallback

Release workflow

The key requirement is still the same: local review, remote deploy of one chosen version, and easy rollback.

release discipline
# local review machine
$ git commit -am "api docs + service release config"
$ git tag api-2026-03-26-01
$ git push origin main --tags

# remote deploy host
$ git fetch --tags
$ git checkout api-2026-03-26-01
$ docker build -t .../data-api:api-2026-03-26-01 .
$ docker push .../data-api:api-2026-03-26-01
$ gcloud run deploy data-api --image .../data-api:api-2026-03-26-01 --no-traffic

# repeat for meta-thinking and sim if changed
$ gcloud run services update-traffic data-api --to-latest

Why this is the right discipline

  • The remote machine deploys an exact tag, not a branch head that may drift.
  • Each API service can move on its own release cadence.
  • You can smoke test a new revision before traffic shifts.
  • The website docs and the runtime release can share one release identifier.

One thing to fix before launch

The curated Data API reference should be reconciled with the live route surface so the product docs and the runtime stay aligned. That matters more now that the Data API is the flagship product and usage-unit billing depends on exact endpoint classification.

What stays on Firebase

All website marketing, pricing, and docs pages. Firebase remains a good home for static releases; it is not the right home for the paid runtime itself.