API
The Ternly API
Everything the app knows about your AI answers, rankings, audits and app listings, as JSON for your own code. Pull it into a dashboard or a client report, or add questions, keywords and competitors and start runs from a script.
Last updated
Before you start
- The API comes with every paid plan: Indie, Studio and Agency. Free workspaces can’t make API keys.
- Make a key on the API tab of the Workspace page. It’s shown once: keep it like a password, on a server, never in a web page or an app you ship.
- A key belongs to one workspace and to you. It sees that workspace only and can do what your role there allows: a viewer’s key only reads.
- The same key works with Ternly’s MCP server, for Cursor and other AI apps that can’t sign in with OAuth.
Your first call
Every address starts with https://app.ternly.io/api/v1. Send the key as a bearer token:
curl https://app.ternly.io/api/v1/sites \
-H "Authorization: Bearer tern_pat_…"
GET https://app.ternly.io/api/v1 answers with the workspace the key is for, your role, and every endpoint you may
call. Here’s a script that adds two questions, asks every AI now, and reads the result when it’s done:
KEY="tern_pat_…"
API=https://app.ternly.io/api/v1
curl -X POST $API/sites/halcyon.app/ai-questions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"questions": ["best hiking app for iPhone", "apps like AllTrails"]}'
curl -X POST $API/sites/halcyon.app/runs/ai -H "Authorization: Bearer $KEY"
# {"crawl_id": 4812, "name": "AI visibility", "status": "queued"}
curl $API/crawls/4812 -H "Authorization: Bearer $KEY" # until "status": "finished"
curl "$API/sites/halcyon.app/ai-visibility?include_answers=true" -H "Authorization: Bearer $KEY"
How it works
- Sites and apps.
{site}is a site’s domain (halcyon.app) or its id fromGET /sites. An app without a website has an id only.{app}is the app’s id fromGET /sites/{site}/apps, its App Store ID, its Google Play package name or its name. - Parameters. A
GETtakes them in the query string;POST,PATCHandDELETEtake a JSON object. A parameter the endpoint doesn’t know is an error, so a typo never passes quietly. - Runs. Starting a run answers at once with a
crawl_id. PollGET /crawls/{crawl_id}until its status isfinished, then read the results. A run that’s already going is returned rather than started twice. - Paid runs (marked below) spend your plan’s monthly allowances, the same as starting them in the app: AI answers, rank checks, pages and briefs.
- Missing values.
nullor a missing field means Ternly doesn’t know it yet, not zero.
Errors and limits
An error answers with a status and a detail that says what to change:
{"detail": "No site 'halcyon.io'. Sites: halcyon.app"}
400a parameter is missing or unknown,422one has the wrong type.401no key, or a key that was revoked or whose person left the workspace.402the plan’s allowance for the month is used up.403a viewer’s key tried to change something, or the workspace is back on Free.404no such site, app, run or brief in this workspace.429too many calls: up to 60 a minute per person, and 30 paid runs a day per workspace (these limits are shared with MCP). Wait forRetry-Afterseconds.
Endpoints
Paths are under https://app.ternly.io/api/v1.
Workspace and sites
- GET
/workspaceThe workspace: its plan, subscription, this month’s usage and its people. - GET
/sitesEvery site and app in the workspace, with its competitors. - POST
/sitesAdd a website (and its brand names and competitors). - GET
/sites/{site}A site at a glance: latest audit, search, rankings and AI answers. - PATCH
/sites/{site}Rename a site, change its website, brand names or settings.
AI answers
- GET
/sites/{site}/ai-visibilityHow each AI answers your questions: mentions, position, share of voice, cited pages.include_answers=trueadds the answers. - GET
/sites/{site}/ai-questionsThe questions asked of each AI, with each AI’s latest result. - POST
/sites/{site}/ai-questionsAdd questions (questions, optionaltags). - PATCH
/sites/{site}/ai-questions/{question_id}Pause, resume or retag a question. - POST
/sites/{site}/ai-questions/removeRemove questions by id (question_ids). - POST
/sites/{site}/ai-questions/suggestPaidHave Claude suggest questions people ask about apps like yours. - GET
/sites/{site}/ai-changesPer-question trends and which cited pages came and went. - GET
/sites/{site}/ai-crawlersVisits from GPTBot, ClaudeBot and other AI crawlers (with Cloudflare connected). - GET
/sites/{site}/brand-profileHow the AIs describe your brand. - POST
/sites/{site}/brand-profilePaidBuild the brand profile again from the latest answers. - GET
/sites/{site}/outdated-answersAnswers with wrong facts or stale cited pages. - POST
/sites/{site}/outdated-answers/checkPaidCheck the latest answers for out-of-date facts now. - POST
/sites/{site}/runs/aiPaidAsk every active question of each AI now. Returns acrawl_id.
To-do
- GET
/sites/{site}/weekly-planThis week’s to-do plan: what to change, where, the text to paste and why. - GET
/sites/{site}/todoEvery open to-do task. - PATCH
/sites/{site}/todo/{key}Mark a task done, skipped or open again (status).
Apps and stores
- GET
/sites/{site}/appsThe site’s apps and competitors’ apps: listings, ratings, store rankings, analytics. - POST
/sites/{site}/appsFollow an app by store link, App Store ID or package name (app,competitor). - DELETE
/sites/{site}/apps/{app}Stop following an app. - GET
/sites/{site}/apps/{app}/reviewsAn app’s reviews, filtered bystarsorsearch. - POST
/sites/{site}/apps/{app}/keywordsTrack store search keywords for an app (keywords). - POST
/sites/{site}/apps/{app}/keywords/removeStop tracking store keywords. - POST
/sites/{site}/runs/app-syncSync the stores and app analytics now. - GET
/sites/{site}/web-app-insightsHow the website and the apps line up: installs from search, pages that lead to the store.
Google keywords and search
- GET
/sites/{site}/keywordsEvery tracked Google keyword with its position, AI Overview and AI answers. - POST
/sites/{site}/keywordsTrack Google keywords, with country, language, engine and device. - POST
/sites/{site}/keywords/removeStop tracking keywords. - POST
/sites/{site}/keywords/pausePause or resume keywords (resume=true). - GET
/sites/{site}/rankingsLatest Google positions and their history. - POST
/sites/{site}/runs/rank-checkPaidCheck every tracked keyword’s position now. - GET
/sites/{site}/keyword-clustersKeywords grouped by topic. - GET
/sites/{site}/search-performanceSearch Console and GA4: clicks, impressions, top pages and queries. - GET
/sites/{site}/search-opportunitiesQueries close to page one, and pages losing clicks. - POST
/sites/{site}/runs/search-consoleSync Search Console and GA4 now.
Competitors
- POST
/sites/{site}/competitorsAdd or remove competitors (add,remove). - GET
/sites/{site}/competitor-watchWhat changed on competitors’ sites. - POST
/sites/{site}/runs/competitor-watchLook at competitors’ sites now. - GET
/sites/{site}/content-gapTopics competitors cover that you don’t. - GET
/sites/{site}/backlinksReferring domains and links, yours and competitors’.
Briefs
- GET
/sites/{site}/briefsWritten briefs, or one withbrief_id. - POST
/sites/{site}/briefsPaidHave Claude write a brief for a keyword or the to-do list. - GET
/sites/{site}/content-briefWhat to cover to rank for akeyword, from the latest results.
Website audits
- GET
/sites/{site}/audit/issuesIssues found by the latest audit, with counts. - GET
/sites/{site}/audit/issues/{check}The pages with one issue (checkis its code). - GET
/sites/{site}/audit/pageEverything the audit found on one page (url). - GET
/sites/{site}/alertsAlerts from the lastdays. - GET
/sites/{site}/indexingGoogle indexing status of the site’s pages. - GET
/sites/{site}/internal-linksInternal links worth adding. - POST
/auditsPaidCrawl a site (url, optionalmax_pages). Returns acrawl_id. - GET
/check-urlLook at one page now: status, title, headings, schema, issues.
Runs (crawls)
- GET
/crawlsRecent runs of every kind (site,mode,limit). - GET
/crawls/{crawl_id}A run’s status and progress: poll it after starting one. - POST
/crawls/{crawl_id}/cancelStop a run.
Managing keys
The API tab of the Workspace page lists every key with when it was last used. Revoke stops one at once. Owners and admins see and can revoke everyone’s; others see their own. Removing someone from the workspace, or moving it to Free, stops their keys too. Each person can have up to 20 keys in a workspace.
Things the API leaves to the app: deleting sites or workspaces, inviting people, changing the plan, and connecting accounts such as Google.
Help
Write to aiden@ternly.io if something doesn’t work, or you need an endpoint that isn’t here.