ASO-Light is a self-hosted, web-based App Store Optimization application — a focused, open alternative to aso.dev. It enables app developers to manage App Store prices across all 175+ territories using economic multipliers (PPP, Big Mac, Netflix, Spotify) and track keyword performance. It can run as a single-operator install or a multi-tenant deployment; user accounts and per-user ASC credentials are first-class.
| Component |
Purpose |
Tech Stack |
| Backend API |
REST API, business logic, ASC integration |
FastAPI (Python 3.12+) |
| Database |
Persistent storage |
SQLite (dev) / PostgreSQL (prod) |
| Frontend SPA |
User interface |
React 19 + Mantine v8 + TypeScript |
| ASC API Client |
App Store Connect integration |
httpx + PyJWT (ES256) |
| Rate Cache API |
Live exchange rates (166 currencies) |
Configurable via RATE_CACHE_API_URL; defaults to the free public api.overx.ai (rate-cache-api, self-hostable) |
| AI Translator |
Claude-backed metadata translation suggestions (Phase 5) |
Anthropic SDK (claude-haiku-4-5) |
Browser (React SPA)
│
│ REST /api/v1/*
▼
FastAPI Backend
│ │ │
▼ ▼ ▼
PostgreSQL Apple ASC API External APIs
(SQLite dev) api.appstoreconnect FX rates API (configurable)
.apple.com World Bank PPP
Economist CSV
iTunes Search
iTunes Hints
Register/Login → JWT (HS256, 30min access + 7day refresh)
│
▼ Authorization: Bearer <token>
FastAPI get_current_user() → user_id
.p8 key (Fernet encrypted at rest)
→ decrypt in memory
→ PyJWT ES256 sign (20min lifetime)
→ Authorization: Bearer <asc_token>
→ api.appstoreconnect.apple.com
backend/app/
├── api/v1/ # Route handlers (thin layer)
│ ├── _deps.py ← Shared: _get_verified_app, _get_asc_client_for_app
│ ├── auth.py
│ ├── credentials.py
│ ├── apps.py
│ ├── pricing.py
│ ├── territories.py
│ ├── keywords.py
│ ├── presets.py
│ ├── export.py
│ ├── availability.py
│ ├── indices.py
│ └── metadata.py ← Phase 5: app metadata CRUD + bulk + translate + coverage
├── services/
│ ├── asc/ # App Store Connect API client
│ │ ├── client.py ← ASCClient base (JWT, pagination, rate limit, throttle)
│ │ ├── apps.py
│ │ ├── pricing.py ← Subs + IAP prices, localizations, screenshots
│ │ ├── price_point_cache.py ← Filesystem cache for Apple price tiers
│ │ └── errors.py
│ ├── pricing/ # Price calculation engine
│ │ ├── calculator.py ← PriceCalculator ABC
│ │ ├── engine.py ← PriceEngine (compose: calc → VAT → charming)
│ │ ├── exchange_rate.py ← ExchangeRateCalculator (live FX rates)
│ │ ├── currency_rounding.py ← Smart currency-aware rounding (±10% flex)
│ │ ├── ppp.py / bigmac.py / netflix.py / spotify.py / fixed_payout.py
│ │ ├── charming.py
│ │ └── vat.py
│ ├── rates/ # Exchange rate API client
│ │ └── client.py ← RateCacheClient (api.overx.ai)
│ ├── indices/ # Economic data fetchers
│ │ ├── base.py ← IndexFetcher ABC
│ │ ├── ppp.py ← World Bank API
│ │ ├── bigmac.py ← Economist GitHub CSV
│ │ ├── netflix.py ← Seed data (~70 countries)
│ │ ├── spotify.py ← Seed data (~70 countries)
│ │ └── refresh.py ← IndexRefreshService orchestrator
│ ├── keywords/ # Keyword analysis
│ │ ├── suggestions.py ← iTunes Search Hints
│ │ ├── itunes_search.py ← iTunes Search API (ranking)
│ │ ├── tracker.py ← KeywordRankingTracker
│ │ └── cross_localization.py ← Static locale→territory map
│ ├── metadata/ # Phase 5: app metadata management
│ │ ├── client.py ← ASCMetadataService (AppInfo + AppStoreVersion read/write + state guard)
│ │ ├── snapshot.py ← MetadataSnapshotService (idempotent ASC → DB sync)
│ │ ├── bulk.py ← BulkMetadataService (preview/apply matrix)
│ │ ├── validation.py ← Char limits + URL validators
│ │ ├── coloring.py ← Pure classify_keyword(...) → 'title'|'subtitle'|'keywords'|'none'
│ │ └── translate.py ← AbstractTranslator ABC + AnthropicTranslator + cache
│ └── export/ # Excel/CSV export+import
│ ├── excel.py
│ └── csv.py
├── models/ # SQLAlchemy 2.0 ORM models
├── schemas/ # Pydantic request/response schemas
├── core/
│ ├── config.py # pydantic-settings (.env)
│ └── security.py # JWT, Fernet, bcrypt
└── data/
├── territories.py # 202 territory seed records
└── seed.py # Idempotent seeding
| Decision |
Choice |
Rationale |
| Private key storage |
Fernet symmetric encryption at rest |
.p8 keys must never be plaintext in DB |
| ASC auth |
ES256 JWT via PyJWT |
Apple requires ES256, not HS256 |
| App auth |
HS256 JWT via python-jose |
Standard SaaS auth |
| Password hashing |
bcrypt (direct, not passlib) |
passlib/bcrypt version incompatibility |
| Price math |
Decimal throughout |
Avoid floating-point rounding errors |
| Calculators |
PriceCalculator ABC + ProportionalCalculator base |
PPP/BigMac/Netflix/Spotify/ExchangeRate share identical formula |
| Currency rounding |
Per-currency profiles with ±10% flex |
JPY/KRW/INR/VND need different rounding than USD/EUR |
| Exchange rates |
Live from api.overx.ai (rate-cache-api) |
166 currencies, cached hourly, no auth needed |
| Price point cache |
Filesystem JSON per territory |
Avoids 20min bulk fetch of ~140k price points |
| ASC rate limiting |
150ms throttle + exponential backoff |
Prevents 429s on bulk operations |
| Frontend tables |
mantine-datatable |
Better virtualization for 175-row price grid |
| DB (dev) |
SQLite + aiosqlite |
Zero-config local development |
| DB (prod) |
PostgreSQL + asyncpg |
Multi-user SaaS needs real DB |
| AI translation |
Anthropic Claude Haiku 4.5 + AbstractTranslator ABC |
Lowest cost for short-form translation; ABC keeps DeepL/OpenAI plug-in ready |
| Translation safety |
Suggestion-only + per-app rolling 30-day soft cap (500) + cache |
No auto-apply ever; bounded spend |
Each user owns their credentials → credentials own apps → apps own all pricing/keyword data. All API endpoints verify the ownership chain: app.credential_id → credential.user_id == current_user_id.
| Data |
Source |
Update Frequency |
| PPP indices |
World Bank API |
Annual |
| Big Mac Index |
Economist GitHub CSV |
Semi-annual |
| Netflix prices |
Seed data (~70 countries) |
Manual/quarterly |
| Spotify prices |
Seed data (~70 countries) |
Manual/quarterly |
| VAT rates |
Seed data (territory model) |
Annual |
| Keyword popularity |
Apple Search Ads API |
Real-time |
| Keyword suggestions |
iTunes Search Hints (unofficial) |
Real-time |
| App rankings |
iTunes Search API |
Real-time |
| Cross-localization |
Static Python dict |
Rare |
| Exchange rates |
rate-cache-api (api.overx.ai) |
Hourly (cached) |