Skip to content

Tracker API endpoint reference

Reference the current Rankability Tracker API routes for projects, scans, result snapshots, trends, and summaries.

Tracker API resources are scoped to the authenticated organization. The legacy /reporter route and reporter:* scope names remain for compatibility. An ID from another organization returns 404 not_found rather than revealing that resource.

Prefix every route with:

https://app.rankability.com/api/agent/v1

Start with Getting started with the Tracker API if you have not yet validated a read-only integration.

Method Path Scope Purpose
GET /reporter/projects reporter:read List projects
POST /reporter/projects reporter:write Create a project
GET /reporter/projects/:id reporter:read Read configuration, SPI, platform summary, scheduling, and latest run
PATCH /reporter/projects/:id reporter:write Update supported project settings
DELETE /reporter/projects/:id reporter:write Permanently delete a project

POST /reporter/projects requires name and a keywords array containing exactly one non-empty string or { "keyword": "...", "targetDomain": "..." } object. One Tracker project represents exactly one durable keyword/report identity. Create separate projects for additional keywords.

Optional fields are:

  • client_id — client UUID in the organization.
  • target_domain and brand_name.
  • gbp_location_name.
  • platforms — defaults to Google organic, Bing, ChatGPT, Perplexity, and Gemini.
  • location, location_lat, and location_lng.
  • grid_size — 3x3, 5x5, or 7x7; defaults to 3x3.
  • language — defaults to en.
  • frequency — daily, weekly, or monthly; defaults to daily.
  • platform_frequencies — platform-specific frequency map.
  • auto_track_enabled — defaults to true; false creates benchmark tracking state.

Send Idempotency-Key when retrying project creation. Rankability enforces the organization’s tracking-project plan allowance and validates client_id ownership. Creating a project does not run an immediate scan; automatic tracking schedules future protected work only when auto_track_enabled is true.

PATCH /reporter/projects/:id can change name, target_domain, brand_name, gbp_location_name, platforms, location, frequency, platform_frequencies, and auto_track_enabled.

Disabling automatic tracking clears scheduling timestamps. Enabling it or changing platforms or frequencies recalculates the schedule.

Send a stable Idempotency-Key for retryable updates and deletes. DELETE permanently removes the tracking project. Treat it as an intentional destructive action; a read-only reporting connector does not need reporter:write.

Project detail supports view=summary|full and fields=.... The backward-compatible default is full. Summary omits the duplicated provenance snapshot while preserving identity, scheduling, SPI, platform measurements, current state, and latest-run information. next_run_at is the earliest real scheduled timestamp for recurring projects and is null for benchmark projects.

Method Path Scope Purpose
GET /reporter/projects/:id/results reporter:read Read grouped platform results
GET /reporter/projects/:id/trends reporter:read Read up to 90 days of terminal-run trends
GET /reporter/projects/:id/sources reporter:read Every ranking and AI-cited page from one scan with whether it mentions the client
GET /reporter/projects/:id/citation-analysis reporter:read Saved page-level analysis of cited or ranking pages
GET /reporter/projects/:id/competitors reporter:read Top organic Google competitor domains for the topic
GET /reporter/scan-status?client_id=... reporter:read Scan state for every Tracker project a client has
GET /reporter/projects/:id/scan-estimate reporter:read Legacy estimate route; pooled accounts should use /usage/estimate
POST /reporter/projects/:id/scan reporter:run Queue a manual scan

GET /reporter/projects/:id/results supports:

  • No query or snapshot=latest_per_platform — newest available successful result for each tracked platform or keyword.
  • snapshot=latest_run — only the newest run.
  • run_id=RUN_ID — one explicit historical run; this overrides the snapshot choice.
  • view=summary: one short line per platform: position, state, mention, citation, URL, and capture time.
  • view=compact — position, mention, citation, URL, state, and change fields without complete answers or provider diagnostics.
  • view=full — the backward-compatible default with complete stored answer and evidence fields when available.
  • platforms=google_organic,local_pack: only these platforms.
  • include_all_results=true: adds all_results to Google, Bing, DuckDuckGo, Brave, and Local Pack rows: the ranked list the app’s Rankings and Local tables show, with position, title, url, domain, and is_client. Local Pack listings also carry rating, reviews, type, address, and phone. all_results_limit caps each list (default 30, maximum 100); total and truncated report the full size.
  • client_missing_only=true: only completed checks where the client does not appear. A failed check is never counted as missing.
  • local_grid=summary|points|all: a Local Pack grid returns the same representative cell the app’s Local table uses, with a grid coverage object (points, failed points, points where the client was found, top-3 points, best and average position). points adds a compact per-point list and all returns every grid row. The default is summary for the summary and compact views and all for the full view.

Every results response names project_id, client_id, keyword, and the primary keyword_id, and each result row carries project_id, so a batch of responses never has to be matched to projects by time.

The response always identifies snapshot.method, latest_run_id, the result-source run IDs, coverage by platform, and the latest run status. The default can intentionally combine results from several runs after a partial or failed attempt.

Each platform also exposes carried_forward_from_latest_run. It is true when the current valid measurement came from an older project run, including when the newest run omitted the platform. using_last_success is narrower: it is true only when the platform’s latest recorded attempt failed and the response falls back to an earlier successful result.

Traditional rows preserve rank, change, URL, and engine data. AI rows can include answer_text, answer_excerpt, brand sentiment data, detected brands, mention position, citations, provider provenance, and capture time. snippet remains a compatibility alias for answer_excerpt.

Every platform snapshot includes separate measurement and history fields. Project-level data_state is not_tracked, no_scan, failed, not_ranked, or measured; failed means the latest attempt ended before producing any result and needs a rerun. Per-platform snapshots are not_tracked, no_scan, failed, not_ranked, or measured. A platform reads failed when its latest attempt failed and no earlier result exists, or when a traditional-search or Local Pack capture returned an empty results list. Result rows for empty captures carry data_state: "failed" and unmeasured_reason: "no_results"; they are never reported as not_ranked, and new scans retry the next provider instead of saving an empty list. Failed empty captures are also excluded from SPI and trend calculations. comparison_state is no_history or comparable when a current measurement exists, and otherwise null. These states are never interchangeable with numeric zero, and no_history is never a data_state value.

Project detail also places those fields directly on every platforms_detail entry, including configured platforms with no result, so consumers do not need to infer measurement state from omission, null, or false. SPI breakdown categories are null when unmeasured, with a parallel category_states map; a returned numeric zero therefore means a measured zero.

Source mentions, citation analysis, and competitors

Section titled “Source mentions, citation analysis, and competitors”

GET /reporter/projects/:id/sources returns the rows behind the app’s source tables for one scan (the newest completed or partial scan unless run_id is supplied). Each row has surface (traditional_search for ranking pages, ai_citation for AI-cited pages), platform, position, url, domain, title, mention_status (none, linked, linked_elsewhere, unlinked, sponsored, error, or null while analysis is pending), analysis_status (unavailable means a login wall that can never be checked automatically), source_type, geo_match, and opportunity (the same page types and markets as /reporter/visibility-gaps), and whether the page is new, continuing, or lost since the previous scan. sponsored means the client appears only in an explicitly paid or featured placement; it does not count as an organic mention and remains an opportunity to get organically listed. analysis.checks separately counts sponsored placements alongside pages that were mentioned organically, not mentioned, could not be checked, sat behind a login wall, or have not been checked yet. Filters: surface, platforms, mention_status (use pending for unanalyzed rows), client_missing_only=true (analyzed pages without an organic client mention, including sponsored-only placements but excluding the client’s own site), and include_lost=true. Results are paginated with limit (default 50, maximum 200) and offset; the first page also includes a domains rollup. view=full adds evidence excerpts, brand sentiment, the brand URL found on the page, and analysis timestamps.

GET /reporter/projects/:id/citation-analysis returns the saved page-level analysis for surface=ai_citation (default) or traditional_search: source type, URL type, mention status, and opportunity status, with the same mention_status, client_missing_only, view, and pagination options.

GET /reporter/projects/:id/competitors?days=30 returns the domains that most often rank in Google for the topic, from the same data as the app’s organic competitor chart, with appearances and latest, best, and average position. include_history=true adds per-scan positions.

GET /reporter/scan-status?client_id=... returns every Tracker project for the client with a scan_state: queued, running, complete, partial, failed_needs_rerun, canceled, or never_scanned. failed_needs_rerun covers failed and stuck scans, so a failed first scan is no longer indistinguishable from a project that was never scanned. Each project also reports needs_rerun, has_prior_data, the latest run with its failed platforms, and the last successful run. The response includes counts per state, terminal, and polling guidance. Optional filters: status=active|archived|all, states=failed_needs_rerun,never_scanned, and attention_only=true.

GET /trends?days=30 defaults to 30 days and caps the request at 90. It includes complete and partial terminal runs. Every point identifies run_status and coverage so consumers can distinguish missing work from a fully completed scan.

GET /scan-estimate returns usage impact for the project’s current platforms, location, Local Pack grid, and tracked keyword count without starting work. A scan runs every keyword on every selected platform, so impact scales with project scope. POST /scan returns a queued run_id; send a stable Idempotency-Key for retryable automation. It rejects archived projects with 400 invalid_operation and returns 409 conflict when a scan is already pending or running.

Method Path Scope Purpose
GET /reporter/summary reporter:read Organization or optional client-level SPI summary
GET /reporter/matrix?client_id=... reporter:read Bounded client keyword × platform position, mention, and citation matrix
GET /reporter/brand-summary?client_id=... reporter:read Client AI mention and citation summary
GET /reporter/visibility-summary?client_id=... reporter:read Client visibility summary: AI mention and citation rates, organic and local pack positions, competitors, and cited domains
GET /reporter/visibility-comparison?client_id=...&before_as_of=... reporter:read Like-for-like visibility change between two dates
GET /reporter/visibility-gaps?client_id=... reporter:read Where the client is missing: pages without the client, AI answers without the client, and search gaps
GET /reporter/seo-performance?client_id=...&days=28 reporter:read Client GSC and GA4 organic-search performance from the client’s connected Google integrations

/reporter/summary accepts an optional client_id. It returns project counts, explicit data/comparison states, average SPI, change from prior measurements when available, platform coverage, and top movers. snapshot_method: "latest_per_platform" means average_spi is calculated from the same current snapshot model as project detail, including valid carried-forward platform results after a partial run. projects_with_history counts projects with at least one comparable platform, while projects_with_spi_delta counts the narrower set whose immediately previous per-platform snapshot supports a complete SPI comparison. A summary with current measurements and no prior scan returns data_state: "measured" with comparison_state: "no_history". average_spi is null when no completed measurement exists, and spi_delta is null when no comparable SPI snapshot exists.

Tracker project-list pagination includes limit, requested_limit, offset, count, total, and has_more. A compact default request can also return limit_cap_reason: "compact_default_fields" when its payload-safety cap applies.

/reporter/matrix requires client_id, defaults to active projects, accepts status=active|archived|all, optional comma-separated platforms, and limit/offset pagination up to 100 rows. It returns one compact row per independent keyword report with project_id, keyword, and explicit measurement, comparison, URL, and freshness fields. Active selection includes legacy projects whose stored status is null. view=summary replaces the per-platform cells with counts of present, missing, failed, and unscanned platforms plus the missing and failed platform lists. client_missing_only=true keeps only completed checks where the client does not appear and drops rows with none; pagination.projects_in_page still reports how many projects the page covered.

/reporter/brand-summary requires client_id. It uses the latest available result per platform and reports projects with and without data, citation and mention rates, counts, and per-platform breakdown. It is an aggregation, not a replacement for the underlying result evidence.

/reporter/visibility-summary requires client_id and reads existing results only; it never starts a scan or uses credits. By default it covers the client’s scheduled reports only (auto-track on and tracking state Monitoring, the same reports Serena counts), so retired, paused, and one-time benchmark reports do not change the rates. A client with no scheduled reports falls back to every active report. scope.mode says which rule applied (scheduled_projects, all_active_projects, or selected_projects) and scope.excluded_unscheduled counts the active reports left out. Pass project_ids (comma-separated, up to 100) to choose projects, including unscheduled ones, subset_project_ids to report a focus group’s rates separately, and as_of to read each project’s latest results captured at or before a date (a bare YYYY-MM-DD means the end of that UTC day). One cell is one project on one platform. Failed and unscanned cells are excluded from rates rather than counted as misses. A cell counts as mentioned when Tracker flagged it, when a brand named in the answer matches the brand name, an alias, or the GBP location name, or when the answer text contains one of those names. A cell counts as cited when Tracker flagged it or any citation URL is on the client’s domain. The local pack position comes from the order of the listings, so the client’s listing is found even when its GBP name differs from the brand name. The default compact view returns one short code per cell (M mentioned, C cited, a number for mention position, . for neither) plus totals, the top five competitors, and the top five cited domains. view=full adds per-cell evidence, URLs, and the complete competitor and domain lists. Add brand aliases and the GBP location name to each project before relying on mention rates.

/reporter/visibility-comparison requires client_id and before_as_of; after_as_of defaults to now. Rates are compared like for like: only cells measured on both dates count, and a cell that failed on one side is excluded rather than reported as a lost mention. The response lists gained and lost mentions and citations, mention position changes, organic and local pack position changes (a positive improvement means the client moved up), new and lost competitors, and caveats such as a brand alias change or no newer scan between the dates. AI answers vary from run to run, so look for movement across several prompts and platforms before drawing conclusions.

/reporter/visibility-gaps requires client_id and reads existing results only; it never starts a scan, fetches a page, or uses credits. It uses the same default scope as the visibility summary (scheduled reports, or every active report when the client has none), or pass project_ids (up to 100) or campaign_id (the campaign’s Tracker cluster or reports; the campaign must belong to the client) to include exactly those reports. The response’s scope block reports the rule used. Results are cached for up to five minutes per client and scope, and a newer scan or finished source check refreshes them sooner; the X-Rankability-Result-Cache header says hit or miss. Check ready before treating the page list as final. source_check_progress reports the source-check status, totals, checked, pending, failed, login-wall, report and completion counts. While ready is false, repeat the same request after retry_after_seconds; the cache fingerprint changes as source checks finish. It returns every page that ranks or is cited by AI in each report’s latest completed scan and does not mention the client, one row per page even when many reports show it. Each page has a page_type (directory_listing, best_of_list, review_platform, info_page, community_thread, competitor_site, lead_gen_doorway, other), a geo_match to the report’s location (match, same_state, other_city_same_name, other_location, unknown), the competitors named on the page when its text was saved, the platforms and number of prompts it shows up for, and a priority and score (search position plus 10 points per AI citation, weighted by page type and market). client_status is missing when the page was checked and the client is not on it, or unverified when the page could not be read (unverified_reason: login_wall, check_failed, or not_checked); check unverified pages by hand. Lead-gen doorway sites, pages about a different city with the same name, and off-topic results are counted in summary.excluded and hidden unless include_excluded=true. When saved page text shows the brand name, an alias, or the GBP location name on a page the stored check scored as no mention, the page counts as present and is listed in mention_check_corrections. Filter with page_types and client_status, and page with limit (default 25, max 100) and offset. The response also lists AI answers that omit the client with the competitors named instead, and queries where the client is outside the organic top 10 or the Local Pack top 3. view=full adds each page’s appearances (report, platform, position) and the ungrouped answer and search rows. Directory list pages are checked as ranked, so a client listed only on a later page of results counts as missing.

/reporter/seo-performance requires client_id and accepts days=7, 28, or 90. It reuses the Google Search Console and GA4 properties already connected to that Rankability client. The response includes current and previous totals, daily trends, GSC queries and pages, submitted sitemaps, and GA4 organic landing pages. Google refresh tokens remain inside Rankability and are never returned to the caller.

  • 400 invalid_input — invalid create/update payload or snapshot value.
  • 400 invalid_operation — a scan was requested for an archived project.
  • 403 forbidden — the key lacks the exact read, run, or write scope.
  • 403 plan_limit_exceeded — project creation exceeds the organization’s tracking allowance.
  • 409 selfserve_ai_query_limit or 409 selfserve_google_keyword_limit — on a self-serve plan, the new project would exceed the tracked AI query or tracked Google keyword allowance. The response includes usage_path to the billing page. Pause an existing query or keyword, or upgrade, before retrying; repeating the request does not help.
  • 404 not_found — project, client, or run is absent or outside the organization.
  • 409 conflict — a scan is already in progress.
  • 429 capacity_reached — Tracker is busy and did not start the scan; retry after the Retry-After interval (60 seconds).
  • 503 queue_unavailable — the scan could not be queued; the response includes the run_id, which is marked failed. Retry shortly.

Review API usage, rate limits, and errors before adding automatic retries or scan schedules.