Skip to main content
An app is one row with a home storefront. Keywords are separate rows scoped by text, store and country, and a join table connects the two. That shape is what lets a single app track keywords in many markets without duplicating the app.

Entities

How do they relate?

A workspace owns apps. Each app captures snapshots of its listing and tracks keywords through a join table, because the same keyword row is shared by every app that tracks it. Traffic and difficulty attach to the keyword, while a position attaches to the pair of an app and a keyword on a given date. Action items belong to the workspace.

Rules that are easy to get wrong

  • An app is one row. Countries live on keyword tracking, not on a second app row. See Countries and markets.
  • Keyword is unique on text, store and country. The same phrase tracked in two markets is two keyword rows checked by two searches.
  • A competitor is an app row too. It carries isCompetitor and points at the primary app it was added to.
  • Traffic and difficulty persist in KeywordMetric. Opportunity does not persist. It is computed per app in the read layer. See Traffic, difficulty and opportunity.
  • installs is a BigInt and serializes as a JSON number.
  • All dates are UTC. Daily granularity uses a PostgreSQL date column, and today means the UTC date.
  • Every tenant owned row carries workspaceId. Multi tenancy is prepared, not implemented, and every account shares the seeded default workspace.

How does the schema change?

Only forward. Every schema change ships as a forward Prisma migration and the API applies pending migrations on boot. There are no hand written SQL migrations and no down migrations, which is why a rollback is a database restore. See Upgrade and roll back.
Last modified on July 31, 2026