The API, without the guesswork.
The same tracking and research used by your dashboard. Here is what is available, how authentication works, and what each request costs.
Authentication
The base path is /api on the same origin as the dashboard. REST app, usage and research endpoints require a Better Auth session cookie. Sign in through the web app before making same-origin browser requests.
REST and MCP use different authentication. An aso_ bearer API key authenticates /mcp, not these REST endpoints. For an AI assistant or API-key-based automation, use the MCP setup guide.
Auth routes live under /api/auth/*, including POST /api/auth/sign-in/email, GET /api/auth/get-session and POST /api/auth/sign-out. Session-creating requests must use your configured app origin. Email verification may be required. Keep session cookies and passwords private.
Check your usage
// Run on the same origin as your signed-in dashboard.
const response = await fetch("/api/usage", {
credentials: "same-origin",
});
if (!response.ok) throw new Error(`Request failed (${response.status})`);
const usage = await response.json();
console.log(usage.research); // { used, limit (null = unlimited), period, resetsAt }Tracking & account endpoints
appId is the numeric ID of your tracked app in Dekadans, not its App Store ID. Get it from GET /api/apps. Supported platforms: iphone, ipad, mac, appletv and realityDevice.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/apps | List your tracked apps. |
| POST | /api/apps | Add an app. Pass appStoreId, or name for an unpublished app; platform and homeStore are optional. |
| GET | /api/apps/:appId/keywords | Read tracked keywords with metrics and up to 30 days of daily history (7 on Free). |
| GET | /api/apps/:appId/keywords/:keywordId/history?days= | Read one keyword's daily history: 7, 30, 90, 365 or all. Free is capped at 7 days. |
| POST | /api/apps/:appId/keywords | Track keywords. Body: { store, keywords: string[] }, with 1–100 keywords. |
| DELETE | /api/apps/:appId/keywords/:keywordId | Permanently delete a tracked keyword and its history. Returns 204 with no body. |
| POST | /api/apps/:appId/refresh | Refresh tracked measurements for an app. |
| GET | /api/usage | Read your plan, app and keyword allowances, and research usage for the current period. |
| GET | /api/research | List the available research tools. |
| POST | /api/research/:tool | Run one research tool with its arguments as the JSON body. |
Adding keywords attempts measurement immediately. A provider failure can still leave the keywords saved; check measurementError in the response before assuming measurements are available.
Research tools
Send a JSON body to POST /api/research/:tool. All tools accept a two-letter store code, defaulting to us. Suggestions, live search and competitor ideas also accept platform, defaulting to iphone. Keyword strings contain 1–200 characters; App Store IDs are numeric strings, not URLs.
| Tool | Required arguments | Returns / options |
|---|---|---|
get_keyword_suggestions | appStoreId | Keyword ideas with popularity and difficulty. highPopularity defaults to true. |
search_app_store | term | Ranked apps and difficulty. Optional appStoreId adds that app’s position. limit: 1–100, default 50. |
get_keyword_popularity | terms: string[] | Popularity for 1–100 keywords. Scores are store-specific, not platform-specific. |
get_competitor_keywords | term | Popular phrases from the top 10 competitors’ names and subtitles. Optional appStoreId excludes your app. |
get_most_searched_keywords | None | Most-searched terms. Optional category ID, page (1–10,000) and limit (1–100). Check the source month. |
get_app_details | appStoreIds: string[] | Listing details for 1–50 App Store IDs. Apps unavailable in the selected store are omitted. |
Compare keyword popularity
// Uses one research call if the request succeeds.
const response = await fetch("/api/research/get_keyword_popularity", {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
store: "us",
terms: ["habit tracker", "daily planner"],
}),
});
const data = await response.json();
if (!response.ok) throw new Error(data.error ?? "Research failed");
console.log(data.keywords); // [{ text, popularity }]Errors & limits
REST errors normally return { "error": "Message" }, with issues for input validation. Auth routes can return message instead. Always check the HTTP status; a successful delete has no JSON body.
| Status | What to do |
|---|---|
400 | Invalid input. Check the issues array for the field and validation message. |
401 | Missing or expired session. Sign in again. For MCP, check your API key. |
403 | App or tracked-keyword plan limit reached. |
404 | Resource not found or not owned by your account. |
409 | Duplicate resource, such as an app you already track. |
429 | Research quota or rate limit reached. Free resets on the 1st, Pro daily, at 00:00 UTC. |
502 / 503 / 504 | Provider failure, missing configuration or timeout. Failed research requests do not consume research calls. |
Every successful research call counts once, including cached responses. Dashboard, REST and MCP share the research allowance. Invalid input and failed requests do not count. Adding apps, tracking keywords and refreshing tracked measurements are not research calls.
MCP keys have an additional limit of 120 requests per minute. Do not retry authentication or validation errors in a loop. For rate limits or temporary upstream failures, wait before retrying; do not assume every response includes a retry time.
Check your usage, compare plan limits, or open the troubleshooting guide.