Developer / REST API

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.

MethodEndpointPurpose
GET/api/appsList your tracked apps.
POST/api/appsAdd an app. Pass appStoreId, or name for an unpublished app; platform and homeStore are optional.
GET/api/apps/:appId/keywordsRead 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/keywordsTrack keywords. Body: { store, keywords: string[] }, with 1–100 keywords.
DELETE/api/apps/:appId/keywords/:keywordIdPermanently delete a tracked keyword and its history. Returns 204 with no body.
POST/api/apps/:appId/refreshRefresh tracked measurements for an app.
GET/api/usageRead your plan, app and keyword allowances, and research usage for the current period.
GET/api/researchList the available research tools.
POST/api/research/:toolRun 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.

ToolRequired argumentsReturns / options
get_keyword_suggestionsappStoreIdKeyword ideas with popularity and difficulty. highPopularity defaults to true.
search_app_storetermRanked apps and difficulty. Optional appStoreId adds that app’s position. limit: 1–100, default 50.
get_keyword_popularityterms: string[]Popularity for 1–100 keywords. Scores are store-specific, not platform-specific.
get_competitor_keywordstermPopular phrases from the top 10 competitors’ names and subtitles. Optional appStoreId excludes your app.
get_most_searched_keywordsNoneMost-searched terms. Optional category ID, page (1–10,000) and limit (1–100). Check the source month.
get_app_detailsappStoreIds: 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.

StatusWhat to do
400Invalid input. Check the issues array for the field and validation message.
401Missing or expired session. Sign in again. For MCP, check your API key.
403App or tracked-keyword plan limit reached.
404Resource not found or not owned by your account.
409Duplicate resource, such as an app you already track.
429Research quota or rate limit reached. Free resets on the 1st, Pro daily, at 00:00 UTC.
502 / 503 / 504Provider 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.