Sunset2025

AppStoreOptimization.ai

An App Store Optimization platform that analyses every tracked keyword each night and pulls a developer’s own App Store Connect data in next to it. It’s now in early access after a 2026 rebuild of its foundations.

Status
Sunset
Ran 2025 — 2026
Built in
Two passes
May — October 2025, rebuilt September 2026
Stack
Next.jsSupabaseVercel WorkflowsVercel AI SDKApp Store Connect API

01 / 05

Keyword research

Popularity, difficulty and your position for every tracked keyword, with the apps ranking for it.

The problem

Keyword rankings, reviews and download data live in different places.

To work on an app’s App Store search ranking, a developer needs to know which keywords are worth targeting, where they rank today, what competitors’ users complain about, and whether impressions and downloads are moving. AppStoreOptimization.ai puts keywords, rankings, competitors and reviews in one place, for indie developers first.

How it works

Add your keywords and connect App Store Connect; the rest updates overnight.

A developer signs in with an emailed code, connects App Store Connect with an API key, and sets up an organisation and projects for their apps and teammates. Each keyword they add is scored for traffic and difficulty, and their rank is found among the top 250 search results, then re-checked every night to build a rank history. The dashboard shows impressions, downloads and conversion from Apple’s own analytics reports, and competitor pages turn that app’s reviews into a list of market gaps.

Building it

It’s a Next.js 16 app on Supabase Postgres, with React Query on the client, background work on Vercel Workflows and AI through Vercel AI Gateway. It began in May 2025 as an AI script-writing tool called Content Engine and became an ASO platform within six weeks. After almost a year away, I came back in September 2026 and rebuilt the foundations: a durable nightly pipeline, a full security pass on the multi-tenant data, a real test suite and a release pipeline, all merged as one 491-file change.

API routes
73
Lines of TypeScript
100k+
Tests, plus a generated RLS suite
450+
Tracked issues fixed in the rebuild
62

Under the hood

Nightly keyword pipeline

Keyword data comes from Apple’s public App Store endpoints. For each keyword, the pipeline scores the top 10 apps and fetches the top 250 results in parallel, then weights the signals into traffic and difficulty scores from 10 to 100. Rank isn’t stored: it’s worked out on read from the saved order of results. Every night a cron claims one batch per UTC date and starts a chain of shard workflows on Vercel Workflows. Each shard takes the next 100 keywords and runs them through a sliding pool of 10. Every analysis is written in one transactional Postgres function keyed on a run id, so a retry can never double-write. Failures back off from 30 seconds to 32 minutes, more than 10% failing alerts Slack, and a 06:00 watchdog flags a batch that’s missing, failed or stalled. It costs about $2.40 a month per 1,000 nightly keywords.

  • The problem

    Silent data loss. The old queue-based job’s query hit PostgREST’s 1,000-row cap, and failed analyses were never retried. With 1,005 keywords due, it analysed exactly 1,000.

    The fix

    Keyset-paginated shards with retries. A test reproduced the 1,000-row cut-off before the fix and passes after it.

  • The problem

    Starting a workflow has no idempotency key, so a retried “start the next shard” step could fork the chain into two.

    The fix

    A shard ledger with a unique key per shard, and a claim-generation fence that a stale or forked chain can’t get past.

  • The problem

    The scraping library never applied its timeout (a 2-second timeout took 29 seconds), and its HTTP errors lost their status codes.

    The fix

    The pipeline wraps every call in its own deadline and treats every failure as retryable instead of trusting the library’s errors.

  • The problem

    Each completed step replays the workflow’s event log, so cost grows with the square of shard size.

    The fix

    Shards of 100 keywords instead of 500, with the chain carrying the batch forward.

App Store Connect integration

A developer uploads their App Store Connect API key. It’s tested with a live call to Apple before it’s stored, then encrypted with AES-256 and a random IV per key. Every request to Apple signs a fresh ES256 JWT that’s never cached. For analytics, the app requests reports from Apple’s Analytics Reports API, walks reports → instances → segments, downloads and unzips the files five at a time, parses them and upserts in batches of 100. A SQL function then rolls them into one summary row per day for impressions, downloads and conversion. The tests run against a fake Apple server that checks every JWT the way Apple does.

  • The problem

    Key material could reach the browser. The credential health check selected every column, the connect route returned the signed JWT, and a route existed just to regenerate one.

    The fix

    An allow-list of public columns, no token in any response, and the regenerate route deleted.

  • The problem

    An analytics request could be attached to any project, including someone else’s.

    The fix

    A project-visibility check that returns 404 before Apple is ever called, covered by an ownership test suite.

Multi-tenant security

Data is organised as organisations → members (owner, admin or member) → projects, and Postgres row-level security enforces it, with one policy per operation per role. The rebuild included a 13-milestone security plan, each milestone followed by its own review-and-fix pass, and it closed 12 of the 14 security items on the tracker.

  • The problem

    Any signed-in user could add themselves to any organisation, or promote themselves within one.

    The fix

    Membership is checked through SECURITY DEFINER helpers in a private schema. That also avoids the infinite recursion you get when a policy queries the table it protects.

  • The problem

    11 of 22 tables had row-level security switched off, including the analytics data and the shared app catalogue.

    The fix

    Per-project policies on every table, with deletes allowed only on aggregate rows.

  • The problem

    Proving the policies actually work, and keep working.

    The fix

    A spec-driven engine generates one test per table, operation and role. A mutation probe breaks a policy on purpose, confirms the tests go red, then restores it and checks a fingerprint. A guard refuses to run the tests against anything but an isolated database.

Release pipeline

Every PR has to build, pass the type and lint ratchet and have a Conventional Commits title, and gets an advisory security review from Claude. Merging deploys to staging after migrating its database first. A release tag deploys production without its domain, smoke-tests the health endpoint against the expected commit, and only then promotes it. Production migrations run by hand, and only once staging has already applied them, which is checked through the Supabase Management API. That’s 10 GitHub Actions workflows.

  • The problem

    The 2025 code carried hundreds of type and lint errors, so strict CI would have blocked every PR from day one.

    The fix

    Builds ignore type errors, and a ratchet fails any PR that adds errors beyond a committed baseline. The baseline has come down from 92 type and 1,279 lint errors to 58 and 772.

  • The problem

    Tags pushed with GitHub’s default token don’t trigger other workflows, so a release tag would never have deployed.

    The fix

    release-please calls the production deploy directly as a reusable workflow, and skips it when the release bot’s own tag would trigger it anyway.

  • The problem

    The old setup pushed migrations to production on every merge.

    The fix

    Staging migrates automatically, and production only migrates from a manual workflow that refuses anything staging hasn’t run.

Where it is now

Sunset. Ran 2025 — 2026.

Production went live on 5 October 2026 in early access, with no real users yet. The pricing page lists Hobby (free), Vibe Coder ($30 a month) and Agency ($99 a month), but billing isn’t built, so every account is free for now. Some pieces are built but not switched on, including scheduled analytics ingestion and replying to reviews, and the first tagged release was never cut. I’ve since sunset it.

Production launch
Oct 2026
Planned monthly price
$0–99
Monthly cost per 1,000 nightly keywords
~$2.40

What I learned

[Your words: what the year away and the rebuild taught you.]

Ask about AppStoreOptimization.ai

The site’s assistant has read this case study. Ask it anything, or start with one of these.

Next projectSubliminalMe ›