SDK Reference
Conventions
A few things hold for every method on this page:
- You destructure
data. Each call returns{ data, request, response };datais the parsed body. TypeScript types it as possiblyundefined, so examples writedata!— see the Quickstart for why. - Envelopes. A single resource returns
{ item }, a list returns{ items }, and a paginated list returns{ items, pagination }wherepaginationis{ page, limit, total, totalPages, hasMore }. Paging is manual. - Errors throw. Any non-2xx response throws a
CompetLabErrorwithstatus,code, andmessage. It’s not a returned value; youcatchit. - Types are exported. Every response type named below (
ProjectDetailResponse,PaginationMeta, and the rest) is exported from@competlab/sdkfor you to import.
Construct the client once and reuse it:
import CompetLab from '@competlab/sdk';
const cl = new CompetLab({ apiKey: process.env.COMPETLAB_API_KEY! });health
Service liveness. The one method that needs no API key.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.health.check() | — | { item: HealthResponse } | GET /v1/health |
projects
Your projects — the top of every other call, since most methods take a projectId.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.projects.list() | — | { items: ProjectListItemResponse[] } | GET /v1/projects |
cl.projects.get(projectId) | projectId: string | { item: ProjectDetailResponse } | GET /v1/projects/{projectId} |
competitors
The competitors tracked within a project.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.competitors.list(projectId) | projectId: string | { items: CompetitorListItemResponse[] } | GET /v1/projects/{projectId}/competitors |
cl.competitors.get(projectId, competitorId) | projectId: string, competitorId: string | { item: CompetitorDetailResponse } | GET /v1/projects/{projectId}/competitors/{competitorId} |
aiVisibility
How the AI engines mention your brand versus competitors — dashboard, history, one check’s detail, and a provider trend over time.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.aiVisibility.dashboard(projectId) | projectId: string | { item: AiVisibilityDashboardResponse } | GET …/ai-visibility |
cl.aiVisibility.history(projectId, query?) | query?: { page?, limit? } | { items: AiVisibilityHistoryItemResponse[], pagination } | GET …/ai-visibility/history |
cl.aiVisibility.checkDetail(projectId, checkId) | projectId: string, checkId: string | { item: AiVisibilityCheckDetailResponse } | GET …/ai-visibility/history/{checkId} |
cl.aiVisibility.trend(projectId, query?) | query?: { dateFrom?, dateTo?, provider? } | { items: AiVisibilityTrendDataPointResponse[] } | GET …/ai-visibility/trend |
The provider filter takes an AiProvider: 'openai' | 'claude' | 'gemini'. Note the wire
value for ChatGPT is 'openai' — cl.aiVisibility.trend(projectId, { provider: 'openai' }).
The trend method returns a plain { items } list, not a paginated one.
positioning
Where you sit in the market narrative — dashboard, history, and one run’s detail.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.positioning.dashboard(projectId) | projectId: string | { item: PositioningDashboardResponse } | GET …/positioning |
cl.positioning.history(projectId, query?) | query?: { page?, limit? } | { items: PositioningHistoryItemResponse[], pagination } | GET …/positioning/history |
cl.positioning.runDetail(projectId, runId) | projectId: string, runId: string | { item: PositioningRunDetailResponse } | GET …/positioning/history/{runId} |
pricing
Competitor pricing and plan changes — dashboard, history, and one run’s detail.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.pricing.dashboard(projectId) | projectId: string | { item: PricingDashboardResponse } | GET …/pricing |
cl.pricing.history(projectId, query?) | query?: { page?, limit? } | { items: PricingHistoryItemResponse[], pagination } | GET …/pricing/history |
cl.pricing.runDetail(projectId, runId) | projectId: string, runId: string | { item: PricingRunDetailResponse } | GET …/pricing/history/{runId} |
content
Competitor content and messaging changes — dashboard, history, run detail, and a changelog of what changed.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.content.dashboard(projectId) | projectId: string | { item: ContentDashboardResponse } | GET …/content |
cl.content.history(projectId, query?) | query?: { page?, limit? } | { items: ContentHistoryItemResponse[], pagination } | GET …/content/history |
cl.content.runDetail(projectId, runId) | projectId: string, runId: string | { item: ContentRunDetailResponse } | GET …/content/history/{runId} |
cl.content.changelog(projectId, query?) | query?: { page?, limit?, competitorId?, category?, allUrlsPerCategory? } | { items: ContentChangelogItemResponse[], pagination, truncated } | GET …/content/changelog |
The changelog adds a truncated boolean to the envelope, set when the result was capped.
techTrust
Tech stack and trust signals — dashboard, history, and one run’s detail.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.techTrust.dashboard(projectId) | projectId: string | { item: TechTrustDashboardResponse } | GET …/tech-trust |
cl.techTrust.history(projectId, query?) | query?: { page?, limit? } | { items: TechTrustHistoryItemResponse[], pagination } | GET …/tech-trust/history |
cl.techTrust.runDetail(projectId, runId) | projectId: string, runId: string | { item: TechTrustRunDetailResponse } | GET …/tech-trust/history/{runId} |
alerts
Notable changes surfaced across the dimensions, filterable.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.alerts.list(projectId, query?) | query?: { page?, limit?, dimension?, severity?, competitorId? } | { items: AlertListItemResponse[], pagination } | GET …/alerts |
dimension is one of tech-trust, content, positioning, pricing, ai-visibility;
severity is one of critical, high, medium, info.
schedules
The monitoring cadence configured for a project.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.schedules.list(projectId) | projectId: string | { items: ScheduleItemResponse[] } | GET …/schedules |
strategicBriefing
The synthesized monthly read across every dimension. Its envelope is the one exception to the
{ item } pattern.
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.strategicBriefing.get(projectId, query?) | query?: { sections?, includeCharts? } | { item, meta, coverage, dimensionHealth } | GET …/strategic-briefing |
Check meta.availability — 'ready' | 'ready-refreshing' | 'preparing' | 'none' — before
reading item, which is null until the first edition is ready. Pass sections to fetch
specific parts (e.g. ['deep-ai-visibility', 'actions']) and includeCharts: true for chart
data.
const { data } = await cl.strategicBriefing.get(projectId, {
sections: ['deep-ai-visibility', 'actions'],
includeCharts: true,
});
if (data!.meta.availability === 'ready') {
console.log(data!.item);
}tools
The free scan tools — the same public scans the MCP server exposes. These
don’t take a projectId; they run against a URL you give them. Three return their result
directly; the other three are asynchronous scans you start and then poll.
Direct (one call, result inline):
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.tools.sitemapVisualizer(body) | body | { item: SitemapVisualizerToolResponse } | POST /v1/tools/sitemap-visualizer |
cl.tools.aiCrawlerChecker(body) | body | { item: AiCrawlerCheckerToolResponse } | POST /v1/tools/ai-crawler-checker |
cl.tools.fetchUrl(body) | body: { url, bodyNeeded?, headersNeeded?, cleanHtml?, maxTimeoutMs?, bodyMaxBytes? } | { item: FetchUrlToolResponse } | POST /v1/tools/fetch-url |
Async scans (start, then poll — see the Quickstart):
| Method | Parameters | Returns | Endpoint |
|---|---|---|---|
cl.tools.techStack.startScan(body) | body (e.g. { domain }) | { item: TechStackScanResponse } | POST /v1/tools/tech-stack/scans |
cl.tools.techStack.getScan(scanId) | scanId: string | { item: TechStackScanResponse } | GET /v1/tools/tech-stack/scans/{scanId} |
cl.tools.trustSignals.startScan(body) | body (e.g. { domain }) | { item: TrustSignalsScanResponse } | POST /v1/tools/trust-signals/scans |
cl.tools.trustSignals.getScan(scanId) | scanId: string | { item: TrustSignalsScanResponse } | GET /v1/tools/trust-signals/scans/{scanId} |
cl.tools.agentAdoption.startScan(body) | body (e.g. { domain }) | { item: AgentAdoptionScanResponse } | POST /v1/tools/agent-adoption/scans |
cl.tools.agentAdoption.getScan(scanId) | scanId: string | { item: AgentAdoptionScanResponse } | GET /v1/tools/agent-adoption/scans/{scanId} |
A scan response carries status — 'queued' | 'running' | 'completed' | 'failed' — with
result present once completed and error present if failed. Scan IDs expire after 24
hours. Of the scan request bodies, only fetchUrl’s is strongly typed; the scanners accept
an open object where the domain field is what they read.
Full method map
All 34 methods, at a glance:
health check
projects list · get
competitors list · get
aiVisibility dashboard · history · checkDetail · trend
positioning dashboard · history · runDetail
pricing dashboard · history · runDetail
content dashboard · history · runDetail · changelog
techTrust dashboard · history · runDetail
alerts list
schedules list
strategicBriefing get
tools sitemapVisualizer · aiCrawlerChecker · fetchUrl
techStack.{startScan,getScan}
trustSignals.{startScan,getScan}
agentAdoption.{startScan,getScan}For the underlying HTTP — status codes, full request and response schemas, error codes — see the REST API reference. The SDK is a typed layer over exactly those endpoints.