Attribura

Keyword tools

Reads are free, tracking is ongoing

13 tools cover the App Store keyword surface — 7 read and 6 write. The reads are ordinary: they answer a read-only token and cost nothing. The writes are not ordinary. Tracking a keyword is not a row you add and forget — it is a standing instruction to go and check that term on the App Store every night from then on. That is what this page is for.

reserve_keyword is not one of these.

Despite the name it has nothing to do with App Store keywords or rankings. It reserves an Instagram comment-gate word — the “comment HEME for the link” trigger — and creates a rule that auto-DMs anyone who says it. An agent told to “add some keywords” can grab it by the name and quietly create an automation on your Instagram account. For the App Store you want track_keywords.

What tracking commits you to

One tracked (keyword, storefront)pair is one App Store check every night, indefinitely. That repetition is the whole product — a rank is only meaningful next to yesterday's — but it means the size of your tracked set is a nightly commitment, not a one-off import.

  • Ranks cannot be fetched in bulk. Apple publishes no ranking API, so a position is read out of live search results one term at a time, slowly and on purpose. A night therefore holds a few thousand checks in all, and adding terms spends from that.
  • Scale by relevance, not by coverage. 40 terms across 37 storefronts is 1,480 checks a night booked by a single call. Ten storefronts you actually sell in beat every storefront a locale sweep turned up.
  • There is no bulk delete. Removal is one keyword at a time (untrack_keyword), and it takes that keyword's recorded history with it — re-adding the same pair starts a new series from tonight, not from where it left off.

Every read tool returns a live budget object — what is booked, what is left, what a batch would add. Read it before adding and you will never meet a refusal; it is current in a way a docs page cannot be.

Storefront vs locale

These are two different axes and no tool here takes a single “market” argument for both, because collapsing them is how a French keyword ends up billed against the American storefront.

Storefront — us, fr

An ISO-3166 country whose App Store search results get queried. Ranks, popularity and the nightly bill live here. One storefront × one keyword = one check a night.

Locale — en-US, fr-FR

The language a store page is written in. Title, subtitle and the 100-character keywords field live here, each with its own hundred characters.

The mapping is many-to-one and cannot be inverted by guessing: Catalan and Spanish both sell into es. Where a locale carries its country (fr-FR fr) that is a fact; where it carries only a language or a script it is an inference, and suggest_keywords says which of the two you are looking at rather than silently defaulting to us and billing a country nobody chose.

The tools

read answers a read-only token; write gets a 403 from one and needs a full-access token. Same rule as everywhere else — the backend gates on the HTTP method underneath, so it is one fact, not a second list.

keyword_briefread

Start here. One call for one storefront + locale: the listing, the tracked set with its scores, the packed field, the candidates and the budget.

country *
Storefront whose ranks, packed field and tracked rows come back.
locale *
Listing locale the store page and the candidate terms are read in.
list_tracked_keywordsread

The tracked set: rank, 7-day trend, popularity, difficulty and relevance per (keyword, storefront) pair. Filterable by storefront.

country
Storefront filter. Omitted, you get every storefront; set, the rows it left out are counted rather than dropped in silence.
get_keyword_historyread

One keyword's rank series over a window, plus the competitors that held the slate and how often each appeared.

id *
The tracked keyword's id.
days
Trailing window, 1–365. Default 30.
get_keywords_fieldread

The packed 100-character keywords field for ONE storefront, best-observed-rank first, with what did not fit. Storefront is required.

country *
Required, not optional: Apple's keywords field belongs to one localisation, so a field packed from every storefront at once mixes languages and is pasteable nowhere.
get_listingread

One locale's App Store listing with per-field editable / lock_reason — check it before drafting a title nobody can apply.

locale
Listing locale. Omit for the app's primary one; the response lists every locale the app has.
suggest_keywordsread

Candidate terms mined from your own live listing. Reads App Store Connect on every call, so don't poll it.

locale
Listing locale to mine. Defaults to the primary.
all_locales
Sweep every locale in one request instead of looping. Each result carries the storefront its terms would be tracked on and whether that was a fact or an inference.
update_listingwrite

Push listing text to the App Store, one locale at a time. Only the fields you send are written; each one comes back with Apple's own verdict.

locale *
Listing locale to write. Each locale is a separate record: writing en-US changes nothing in fr-FR.
fields *
Apple attribute name → new value. Only the keys you send are written; null clears a field, and an empty string is refused on the URLs. Check `editable` on get_listing first — on a live app, promotional text is the only field that will be accepted.
create_app_versionwrite

Open a new App Store version — the draft that unlocks every field a live listing freezes. Submits nothing, releases nothing.

version_string *
The version number to create, like 1.4.0. It must sort above the released one, and only one draft version can exist at a time.
locale
Which locale to read back in the returned listing. Defaults to the app's primary one.
list_notesread

What changed and when, newest first: a listing field written through Attribura (before → after), a version Apple reports on sale, a note someone added. Read a rank move next to the note that precedes it.

add_notewrite

Record a change made elsewhere — a price, a campaign, a partnership — on its date, so the charts and the history carry it. Listing writes are noted on their own, and so is every version once Apple reports it on sale.

delete_notewrite

Remove one note that was wrong. Notes are never edited: delete, then add again.

track_keywordswrite

Start tracking (keyword, storefront) pairs. Each one is an Apple request every night, forever — the server refuses a batch that costs too much.

pairs *
Explicit (keyword, storefront) pairs — the only shape accepted. A {keywords} × {countries} cross product is unreachable on purpose: 30 terms and 30 storefronts is 900 nightly requests from a call that reads like it asked for 30 things.
untrack_keywordwrite

Stop tracking one pair and delete its rank history. One per call — there is no bulk delete.

id *
The tracked keyword's id. One per call — there is no bulk delete.

* required. keyword_briefis pure aggregation — it wraps the listing, the tracked set, the packed field, the candidates and the budget into one round trip and decides nothing. Nothing on this surface scores or ranks anything for you: popularity is Apple's own number and is frequently absent, difficulty and relevance are Attribura estimates, and each ships with the recipe that produced it so you can argue with the arithmetic instead of trusting a badge.

get_listing, suggest_keywords and therefore keyword_brief read App Store Connect live on every call. They are briefing calls, not polls. Everything else reads what the nightly collector already stored and touches no Apple API at all.

Notes

A note is a change on its date. Every accepted update_listingfield writes one on its own (what changed, before → after, per locale); a version gets one the day Apple reports it on sale, with Apple's own release date and release notes; add_note is for anything else. get_keyword_history returns the notes inside its window beside the rank series, and the Overview and Keywords charts stack them on the release that took them live. A listing change made directly in App Store Connect is not noted.

When it refuses

A refused write comes back as a refusal, not as an error worth retrying — the server writes the sentence and the tool passes it through with the plumbing stripped off.

  • Over the per-call limit — one call may book at most 200 new checks. Split the batch, or narrow the storefronts.
  • Over tonight's remaining capacity — the batch asks for more checks than the night has left. The message says how many are free, so the fix is arithmetic: ask for that many, or untrack terms you no longer watch.
  • 403 on a write — not a bug. You are holding a read-only token; all 7 read tools here still work.

Both limits live in the API rather than in the panel, deliberately: a greyed-out button is a suggestion, since the same token reaches these routes from curl, from a script and from these tools. Re-sending pairs you already track is idempotent and costs nothing, so a retry after a timeout is never refused for work it does not incur.

The rest of the tools are in the catalog. The same keyword data is reachable over plain HTTP — GET /v1/keywords, /v1/keywords/:id/history, /v1/keywords/:id/ranking, /v1/keywords/field?country=, /v1/keywords/suggestions and /v1/store/listing?locale= — if you would rather curl than call a tool.