Keeley Keys API
Give your agent an app link, an idea, or a competitor list. Get keyword research and ready-to-use App Store listing options.
Base URL: https://aso.hugmediary.com
Service setup: choose the App Store country for each research request. The default is the United States. Check /health for research_available and supported_countries. An API key is provided when access is activated.
Connect your agent · Agent instructions
Quick start
- Get your API key. Use
https://aso.hugmediary.comas the base URL. - Submit your app or idea to
POST /v1/research. - Check the returned job with
GET /v1/jobs/{job_id}. - Retrieve the report and download the formats you need.
Supply your credentials through your agent's secret configuration. Use HTTPS and the Authorization: Bearer header. Never put the key into a prompt, URL, generated report or public repository.
The following is an illustrative request for an unpublished app. Set ASO_KIT_BASE_URL, ASO_KIT_API_KEY and a unique ASO_KIT_REQUEST_ID through your own configuration before running it. Set ASO_KIT_BASE_URL=https://aso.hugmediary.com; keep credentials in your secret configuration.
curl --request POST "$ASO_KIT_BASE_URL/v1/research" \
--header "Authorization: Bearer $ASO_KIT_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $ASO_KIT_REQUEST_ID" \
--data '{
"description": "Relaxing puzzle games for short breaks",
"app_name": "Pause",
"include_app_name_in_title": true,
"country": "us",
"variants_limit": 10,
"exports": ["markdown", "xlsx", "csv"]
}'
The response is 202 Accepted with the job ID, status, queue information and replayed. Save the job ID before polling. A job is not a finished report; successful completion supplies report_id. Repeating the same operation with the same idempotency key returns its existing job without consuming another submission.
What you can send
Provide at least one of description, app_url or a nonempty competitor_urls list.
| Field | Type | Purpose |
|---|---|---|
description |
string | Describe the app, audience or idea. The app does not need to be published. |
app_url |
string | An App Store link for the target app. |
competitor_urls |
string array | An optional explicit competitor list. Supplying it disables automatic competitor selection. Can be used without a target app. |
app_name |
string | The name you want to use; overrides the name read from an existing app. |
include_app_name_in_title |
boolean | Keep the supplied app name in title options. Defaults to false; requires app_name when true. |
country |
string | Lowercase two-letter App Store storefront code, such as us, gb, de, ru or jp; defaults to us. See /health for accepted countries. |
competitors_limit |
integer | Requested upper bound for automatic selection, 1–10; defaults to 5. Up to 10 explicit competitors. The operator may configure a lower limit. |
variants_limit |
integer | Maximum listing options requested, 1–100; defaults to 10. Fewer may be returned if fewer valid options exist. The operator may configure a lower limit. |
exports |
string array | Unique requested formats: markdown, xlsx, csv. Defaults to all three. |
App links must use HTTPS on apps.apple.com and include an App Store ID. A target app cannot also be a competitor; duplicate competitors are rejected. Unknown input fields are rejected. Names preserve spelling and punctuation and may contain up to 30 characters, without control characters or surrounding whitespace. Request bodies are limited to 64 KiB.
Without explicit competitors, the service finds relevant apps and prioritizes candidates by rating count. The report includes the selected competitors so you can inspect the research basis.
Each research job covers one country. Request separate jobs for other markets; keep their keywords and popularity data separate. A country and a listing language are different concepts. Country-specific research does not imply that every language is indexed in every storefront.
Set "country": "gb" for the United Kingdom, "de" for Germany or another accepted storefront code. The country inside an input App Store link does not override this field. Cards, competitor discovery, recommendations and keyword metrics use the requested market. An app unavailable there or missing provider data produces a clear failure or a partial report; the service never silently falls back to US.
Countries follow the Apple storefront list and AppFollow country parameters. An accepted country code means a request can be submitted, not that every app or keyword has data there.
The generator uses the actual keywords returned for that country. It does not translate descriptions, keywords or listing fields automatically. For discovery, describe the niche using terms relevant to the chosen market, or supply competitors explicitly. The current vocabulary cleaner has English plural normalization and English/Russian stop words; other scripts are preserved without language-specific translation or morphology.
What you get
Keyword research
The full list of search phrases where at least one selected competitor ranks in positions 1–10, using the current popularity-above-5 filter. Rows retain available popularity, competitor positions and provenance.
Popularity helps compare search terms. It is a source metric, not a count of searches or downloads, and it does not guarantee a ranking. Keep the country and collection date with the values. Missing values remain missing; they are not converted to zero.
Listing options
Ready-to-use combinations of:
- Title, with your app name when requested.
- Subtitle.
- Keywords, as a comma-separated string for the App Store field.
- Character counts, covered-query counts and available coverage scores.
Options fit the selected generator's field limits: title up to 30 characters, subtitle up to 30 and keywords up to 100. A name that cannot fit produces a validation problem instead of being silently shortened.
The service distributes useful words across the three fields so you can compare complete options instead of fitting every word by hand. Coverage describes the collected phrases represented by an option; it is not a forecast of traffic or ranking.
The full research list and an option's keywords field are different outputs. The first contains search phrases and evidence. The second fills one limited listing field.
Reports and files
Markdown for agent reading, XLSX for reviewing the workbook, and CSV for tables. A CSV export may include separate tables for keywords, variants and vocabulary; do not assume the complete report fits into one CSV file. Reports also include available competitor app details from Apple.
The XLSX workbook's first sheet puts the full keyword list and popularity on the left, vocabulary in the middle and up to five best-scoring title/subtitle/keywords options on the right, with character counts. The other sheets retain the complete variants, Top 10 keywords, vocabulary and app details.
API reference
All routes below are relative to the base URL provided with access. Send the bearer key with API requests, including reports, export discovery and file downloads. /health, this documentation, /docs.md, /agents.md and /openapi.json are public. /reference/ provides an interactive schema reference.
| Method | Route | Result |
|---|---|---|
POST |
/v1/research |
Start a research job from an app, description or competitors. |
GET |
/v1/jobs/{job_id} |
Read progress, terminal state, report ID or error details. |
GET |
/v1/reports/{report_id} |
Read the research summary, selected competitors, keyword data and listing options. Keyword rows are paginated; use offset and limit. |
POST |
/v1/reports/{report_id}/variants |
Request new listing options from an existing report without recollecting source data. Returns another job to check. |
GET |
/v1/reports/{report_id}/exports |
Discover the available export files and download locations. |
GET |
/v1/reports/{report_id}/files/{file_index} |
Download an export with the same bearer key. Use URLs from export discovery, not guessed indexes. |
GET |
/v1/usage |
Read the assigned allowance, used and remaining task submissions, UTC month and subscription expiry. |
Keys are scoped to their own jobs, reports and exports. A different key receives 404 for those identifiers, even if it belongs to the same person. Reusing a report across keys is not supported in this version.
Report response
The report includes report_id, job_id, country, device, collected_at, source, competitors, keywords, variants, vocabulary, warnings, partial and the research input. Fresh app-based research also includes available target_app and automatic selection seed_queries.
Keyword rows expose keyword, popularity, rank, app, app_url, ext_id and provenance. Each variant includes variant_id, the three listing fields, title_chars, subtitle_chars, keywords_chars, balanced_score, covered_phrase_count, coverage percentages and example covered phrases.
keywords contains the requested page. keyword_pagination supplies total, offset, limit and next_offset; defaults are offset 0 and limit 200, with a maximum page of 1000 rows. Use the exports for complete files.
Export discovery returns report_id and files. Each file has filename, format, media_type and a relative download_url. Resolve it against your trusted API base URL and send the bearer key to that host only. No provider logs or checkpoints are exposed as exports.
For new variants, submit app_name, include_app_name_in_title and variants_limit as needed. The existing report keeps its original country and source date. Regenerating options does not refresh the keyword metrics.
Jobs
| State | Client action |
|---|---|
queued |
Save the ID and wait before checking again. |
running |
Continue checking the same job at a bounded interval. |
completed |
Retrieve the report identified by report_id. |
partial |
Retrieve the available report and disclose missing data and warnings. Do not present it as complete. |
failed |
Surface the failure. Do not silently create a replacement job. |
Job responses include created_at, updated_at, started_at, finished_at, queue_wait_seconds and run_seconds. Timing is measured from the service's actual timestamps; an unfinished task has no finish timestamp. Runtime is wall-clock time from first worker start and includes a restart or cleanup delay. Older jobs created before timing was introduced may have missing start/finish measurements.
queue_position is 1-based while waiting, 0 while running and null after completion. jobs_ahead counts work ahead in the shared queue. estimated_queue_wait_seconds and estimated_run_seconds give indicative minimum/maximum seconds. Runtime estimates use up to 20 recently completed jobs of the same type when available, with a margin around the observed durations; sparse history retains the broader initial range. samples and basis identify their source. With no measured history, initial ranges are 60–300 seconds for fresh research and 5–60 seconds for regeneration. A small fresh collection has taken roughly one to three minutes in actual runs; market, competitors, tracking capacity and queue length can make it longer. These ranges are guidance, not deadlines or guarantees. If a running task has exceeded its observed range, or cleanup holds the queue, the wait estimate is null rather than an invented completion time.
Use poll_after_seconds and the Retry-After response header. The default is five seconds, ten seconds when work is ahead and fifteen seconds while cleanup pauses the queue. Stop waiting after your own deadline and preserve the job ID so the task can be resumed. Do not submit duplicates while waiting.
During provider cleanup, a running job may show stage: "cleaning_up" and an actionable error. It retains exclusive access; later jobs stay queued until cleanup is confirmed. Temporary keyword batches are backed up, their metrics are saved locally, and only service-added keywords are removed. Existing keywords and apps are preserved. Do not create a new job to work around a pending cleanup.
Retries and errors
Use one unique Idempotency-Key per intended POST operation. The header is required and must contain 8–128 printable ASCII characters without whitespace. Reuse that key and the same body if the transport fails and the outcome is uncertain. A changed operation returns 409. Keys are scoped to the API key and retained with their job records; there is currently no automatic job deletion.
| HTTP status | Meaning and action |
|---|---|
400 / 422 |
Invalid request. Read the error and correct the input. |
401 |
Missing or invalid key. Stop and check access configuration. |
403 |
Access is unavailable for this operation. Surface the reason. |
404 |
Job or report not found or unavailable to this key. Check the identifier. |
409 |
Operation conflict, including an idempotency key used with a different body. |
429 |
Rate or capacity limit. Honor Retry-After when present; distinguish temporary throttling from exhausted subscription allowance. |
503 |
Fresh research is disabled or the dedicated account configuration is missing. Do not retry in a loop; ask the operator to enable collection. |
5xx |
Service failure. Retry reads with a bounded backoff; retry writes only with the same idempotency key. |
Operational errors use {"error":{"code":"...","message":"..."}}. Input validation errors use FastAPI's detail array. A failed job exposes its failure as the job's error string. /health lists accepted storefront countries. The default deployment accepts all supported storefront codes; an operator can temporarily restrict the list. Invalid or disabled countries return 422 unsupported_country before a job is created or a task submission is counted.
The default per-key admission limit is ten queued/running jobs and may be changed by the operator. Jobs execute sequentially through one persistent FIFO queue because they share the provider account. Waiting jobs do not modify that workspace. A restart resumes interrupted jobs with their original IDs and saved collection checkpoints.
Monthly allowance
Each key has an operator-assigned allowance of accepted task submissions per UTC calendar month. For example, a key assigned 100 submissions can enqueue 100 new tasks that month. Read /v1/usage for your actual allowance. Both POST /v1/research and POST /v1/reports/{report_id}/variants consume one submission when a new job is accepted. Completion, partial results or later failure do not change that count. A replay with the same idempotency key and identical body consumes no additional submission. Rejected input, unavailable collection, exhausted quota or admission limits do not create a job and do not consume a submission. Status checks, report reads, export discovery and downloads are free.
GET /v1/usage returns monthly_task_submissions, used, remaining, period, subscription_valid_until and billing_unit: "task_submission". The older monthly_research_jobs field is retained as an alias for the assigned allowance; it now means task submissions, including regeneration. Operator-assigned key limits may differ. Historic uncharged jobs are not retroactively charged when this accounting rule is introduced. Revoked or expired keys return 403.
Request records
The operator keeps durable request records for API calls, static pages, errors and downloads: timestamp, request ID, key identifier/fingerprint, method, path, HTTP status and response duration. Response duration includes file delivery through the server. Records may include bounded research inputs such as the description, app link, competitor links, name, country and requested formats. Authorization and cookie headers, URL queries and raw request bodies are not stored. Do not include secrets in research descriptions or app names. An X-Request-ID response header helps the operator locate a request.
For agents
Start with the user's app link or description. Keep an explicit competitor list and requested name intact. Use US unless another market was requested. Submit one job per country, keep its ID, wait for completion, then fetch the report and files.
Present recommended title, subtitle and keywords together, with their character counts. Attach the full keyword research and popularity data. Explain partial results and missing metrics; do not invent them. Never change an App Store listing without the user's authorization.
Read or download the focused agent instructions. Raw documentation is served at /docs.md, and the focused guide at /agents.md; neither requires JavaScript or an API key. A service URL is still required to access these paths outside the local preview.
Access
Connect your agent to arrange access, an API key and connection instructions. Allowances are agreed directly. Key issuance and payment are not automated yet.