changedInput schema / properties / include / description
Previous value: -"Extra blocks. `history` adds a day-by-day series per term, capped at 25 terms — pair it with `keyword_ids` for specific terms. `visibility` adds the daily top-1/3/10/30/50/100 counts and their change, which is the number that survives a corpus changing size. `shared` adds the terms two of your own apps are both on, and needs `scope: \"portfolio\"`."New value: +"Extra blocks. `history` adds a day-by-day series per term, capped at 25 terms — pair it with `keyword_ids` for specific terms. `visibility` adds **Search Visibility** — the 0–100 answer to \"is our ASO working here\", its like-for-like change and the terms that moved it, plus a line per storefront — and each row's `points_change` and `brand`; also the daily top-N counts, which are over every term the app appeared for, not over this corpus. `shared` adds the terms two of your own apps are both on, and needs `scope: \"portfolio\"`."
addedOutput schema / properties / keywords / items / properties / brand
Added value: +{
+ "description": "Whether this term is somebody's name, for Search Visibility. Present only with `include: [\"visibility\"]` at app scope.",
+ "properties": {
+ "kind": {
+ "description": "Null = scored. `own` = the app's own name; `competitor` = the name of the app ranking #1 for it. Either is listed and left out of Search Visibility.",
+ "enum": [
+ "own",
+ "competitor"
+ ],
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "override": {
+ "description": "The owner's decision, set with tag_keywords `brand`; `none` = counted whatever detection says. Null = detection decides.",
+ "enum": [
+ "own",
+ "competitor",
+ "none"
+ ],
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "reason": {
+ "description": "Why detection flagged it, e.g. \"matches “Fuelio: MPG & Mileage Tracker”, which ranks #1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+}
addedOutput schema / properties / keywords / items / properties / points_change
Added value: +{
+ "description": "What this term did to Search Visibility in its storefront over the window, in points — its exact share of `visibility.score.change`, so the rows' values add up to it. Present only with `include: [\"visibility\"]` at app scope; null for a brand term, a term not measured in both weeks, or when visibility was not asked for.",
+ "type": [
+ "number",
+ "null"
+ ]
+}
changedOutput schema / properties / visibility / description
Previous value: -"Terms in each band at the end of the window, cumulative, when `include: [\"visibility\"]` was asked for. **A state, not a sum** — the last day that has a row, because summing thirty days of \"terms in the top ten\" counts the same term thirty times. Null when it was not asked for, or when nothing has been rolled up for this app yet."New value: +"When `include: [\"visibility\"]` was asked for: Search Visibility (`score`, `storefronts`) and the daily band counts. **The band counts are not over this corpus**: they count every term the app appeared for that day in this storefront, tracked or not, so they move with whatever anybody crawled and can disagree with the rows (a corpus whose rows show 27 terms in the top 10 can read `top10: 20`). Read them as reach across the store, never as the corpus's state. Each is the last day with a row — a state, not a sum — and null when nothing has been rolled up. The whole block is null when visibility was not asked for."
changedOutput schema / properties / visibility / properties / ranked / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
addedOutput schema / properties / visibility / properties / score
Added value: +{
+ "description": "**Search Visibility** in the storefront asked about: 0–100, 100 = #1 on every non-brand tracked term, with its like-for-like `change`, the terms that moved it (`biggest_losses`, `biggest_gains`) and the rivals in `compare_with` scored on the same terms. **Lead with this for \"is our ASO working\"** — counting risers and fallers points the wrong way when a term at #1 drops to #7. Over every tracked term in the storefront, whatever filter the rows had. Null at portfolio scope (niches do not add up) or when the read failed — the caveats say which.",
+ "properties": {
+ "band": {
+ "description": "Banded on effective rank: dominant 60+ (≈#1–2 everywhere, owns the first screen), strong 35–59 (≈#2–4), visible 18–34 (≈#4–10), weak 9–17 (≈#10–25), invisible 0–8 (beyond #25). Null with `score`.",
+ "enum": [
+ "dominant",
+ "strong",
+ "visible",
+ "weak",
+ "invisible"
+ ],
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "biggest_gains": {
+ "description": "The terms that added the most points, best first, at most 5.",
+ "items": {
+ "properties": {
+ "keyword_id": {
+ "description": "Apptail keyword id.",
+ "type": "integer"
+ },
+ "place_now": {
+ "description": "Its place on the last day of the headline week. Null = not in the top 50 (it was measured).",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "place_then": {
+ "description": "Its place on the last day of the earlier week. Null = not in the top 50 (it was measured).",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "points_change": {
+ "description": "What it did to the score, in points. These add up to `change` across all terms.",
+ "type": "number"
+ },
+ "term": {
+ "description": "The term.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "biggest_losses": {
+ "description": "The terms that cost the most points, worst first, at most 5 — the answer to \"why did it fall\". Counted over the whole storefront corpus, whatever filter the rows had.",
+ "items": {
+ "properties": {
+ "keyword_id": {
+ "description": "Apptail keyword id.",
+ "type": "integer"
+ },
+ "place_now": {
+ "description": "Its place on the last day of the headline week. Null = not in the top 50 (it was measured).",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "place_then": {
+ "description": "Its place on the last day of the earlier week. Null = not in the top 50 (it was measured).",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "points_change": {
+ "description": "What it did to the score, in points. These add up to `change` across all terms.",
+ "type": "number"
+ },
+ "term": {
+ "description": "The term.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "change": {
+ "description": "**Like-for-like** change in points: over the terms measured in both weeks only, so adding or removing terms cannot move it. Each row's `points_change` is its exact share. Null when the earlier week has no baseline — not a change of zero.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "compared": {
+ "description": "The apps in `compare_with`, scored on this app's corpus — the same rule as the rows' `competitors`. Empty when nothing was compared.",
+ "items": {
+ "properties": {
+ "app_id": {
+ "description": "Apptail id of the rival from `compare_with`.",
+ "type": "integer"
+ },
+ "band": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "change": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Its name.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "previous": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "score": {
+ "description": "Its score on YOUR terms, same weights, same weeks.",
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "corpus_change": {
+ "description": "The list moving rather than the app. History is recomputed over today's list, so a term added last week still has its earlier places if anyone crawled it; only terms with no earlier crawl land here.",
+ "properties": {
+ "added": {
+ "description": "Terms scored now that were not measured in the earlier week.",
+ "type": "integer"
+ },
+ "effect": {
+ "description": "What they did to `score − previous`, in points. Never part of `change`.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "removed": {
+ "description": "Terms measured in the earlier week that are not now.",
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "country": {
+ "description": "The storefront this score is for.",
+ "type": "string"
+ },
+ "effective_rank": {
+ "description": "The single place that would earn this score on every term: S = 21 is \"as visible as ranking about #8 everywhere\". The sentence to put beside the number. Null for no score or a score of 0.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "formula_version": {
+ "description": "Changes when a constant behind the score changes; scores from different versions do not compare.",
+ "type": "string"
+ },
+ "gained": {
+ "description": "Sum of the positive `points_change` — what the terms that improved added.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "last_measured_on": {
+ "description": "The most recent full-depth crawl of any of these terms, `YYYY-MM-DD`.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lost": {
+ "description": "Sum of the negative `points_change`. `gained + lost = change`.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "previous": {
+ "description": "The same score over the 7 days before the window began. `score − previous = change + corpus_change.effect`.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "reason": {
+ "description": "Why `score` is null, with the fix — \"Track 5 or more non-brand terms in DE to get a score\". Null when there is a score.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "score": {
+ "description": "0–100: the app's weighted search places over the most its non-brand tracked terms allow. **100 = #1 on every one.** The mean of the last 7 days of the window. Null when fewer than 5 non-brand terms were measured — `reason` says why — and never 0 for missing: 0 is a measurement, nothing in the top 50.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "terms_brand_excluded": {
+ "description": "Brand terms listed and left out: the app's own name (it ranks #1 whatever its ASO does) and rivals' names where that rival ranks #1. Detected from title words; an owner can override per term with tag_keywords `brand`. Rows carry `brand`.",
+ "type": "integer"
+ },
+ "terms_scored": {
+ "description": "Non-brand terms measured in the last 7 days — what `score` is over. Quote it beside the score.",
+ "type": "integer"
+ },
+ "terms_unmeasured": {
+ "description": "Non-brand terms with no full-depth crawl in the last 7 days. Out of this week's score — not counted as zero.",
+ "type": "integer"
+ },
+ "weeks": {
+ "description": "The two 7-day spans `score` and `previous` are over, `YYYY-MM-DD`.",
+ "properties": {
+ "now_from": {
+ "type": "string"
+ },
+ "now_to": {
+ "type": "string"
+ },
+ "then_from": {
+ "type": "string"
+ },
+ "then_to": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "country",
+ "biggest_losses",
+ "biggest_gains",
+ "terms_scored",
+ "terms_brand_excluded",
+ "terms_unmeasured",
+ "corpus_change",
+ "compared",
+ "weeks",
+ "formula_version"
+ ],
+ "type": [
+ "object",
+ "null"
+ ]
+}
addedOutput schema / properties / visibility / properties / storefronts
Added value: +{
+ "description": "One line per storefront the app tracks terms in — score, change, band — so \"where is it working\" is one call. Each is its own reading over its own terms; never add them up. Empty at portfolio scope.",
+ "items": {
+ "properties": {
+ "band": {
+ "enum": [
+ "dominant",
+ "strong",
+ "visible",
+ "weak",
+ "invisible"
+ ],
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "change": {
+ "description": "Like-for-like change in points. Null = no baseline.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "country": {
+ "description": "Storefront code.",
+ "type": "string"
+ },
+ "reason": {
+ "description": "Why there is no score here, with the fix.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "score": {
+ "description": "0–100, as `visibility.score.score`. Null with a `reason`.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "terms_scored": {
+ "description": "Non-brand terms the score is over.",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "country",
+ "terms_scored"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+}
changedOutput schema / properties / visibility / properties / top1 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
changedOutput schema / properties / visibility / properties / top10 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
changedOutput schema / properties / visibility / properties / top100 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
changedOutput schema / properties / visibility / properties / top3 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
changedOutput schema / properties / visibility / properties / top30 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
changedOutput schema / properties / visibility / properties / top50 / type
Previous value: -"integer"New value: +[
+ "integer",
+ "null"
+]
addedOutput schema / properties / visibility / required
Added value: +[
+ "storefronts"
+]