Skip to Content
DevelopersSDKQuickstart

SDK Quickstart

Before you begin

New to CompetLab? Create a free account  and set up your first project first — the calls below read your projects, so you need one to get data back.

You need Node.js 20 or newer and a CompetLab API key. Create the key in the dashboard under Settings → API Keys — it starts with cl_live_ and is 40 characters. Copy it when it’s shown; you won’t be able to read it again. (More in Authentication.)

Install

npm install @competlab/sdk

Zero runtime dependencies, ESM and CommonJS both supported.

Construct a client

The API key is the one required option. Keep it in an environment variable and pass it in — the SDK doesn’t read the environment for you:

import CompetLab from '@competlab/sdk'; const cl = new CompetLab({ apiKey: process.env.COMPETLAB_API_KEY! });

The ! is there because process.env values are typed as possibly undefined while apiKey is a required string. In production you’d check the variable is set and fail loudly if it isn’t, rather than assert it.

List your projects

Most methods take a projectId, and cl.projects.list() is where you get one:

const { data } = await cl.projects.list(); for (const project of data!.items) { console.log(project.id, project.name, project.domain); }

Two things to notice, both explained below: you destructure data, and you write data!.

Pull a dimension

With a projectId, read any of the five monitored dimensions. Here’s the latest pricing intelligence:

const projectId = '65a1b2c3d4e5f6a7b8c9d0e1'; const { data } = await cl.pricing.dashboard(projectId); console.log(data!.item.summary);

Swap pricing for aiVisibility, positioning, content, or techTrust to read the other dimensions. A single dimension resource comes back as { item }; a list comes back as { items }.

For the synthesized read across all of them, call cl.strategicBriefing.get(projectId) — but it’s the one exception to the { item } shape. It returns { item, meta, coverage, dimensionHealth }, and item is null until the first briefing edition is ready, so branch on meta.availability before reading it:

const { data } = await cl.strategicBriefing.get(projectId); if (data!.meta.availability === 'ready') { console.log(data!.item); }

The projectId above is illustrative. Real IDs are 24-character hex strings — use one from your own cl.projects.list() response.

Why data and why data!

Every method returns an object with the parsed data plus the raw request and response, so you pull out data. TypeScript types data as possibly undefined, which is why the examples assert it with !. At runtime the client throws on any non-2xx response, so if a call returns without throwing, data is always present — the ! just tells the compiler what the runtime already guarantees. If you’d rather not assert, narrow it explicitly:

const { data } = await cl.projects.list(); if (!data) throw new Error('unreachable — a returned result is always a success'); console.log(data.items.length);

Handle errors

Any non-2xx response throws a typed CompetLabError. Catch it and branch on status or code:

import CompetLab, { CompetLabError } from '@competlab/sdk'; try { const { data } = await cl.projects.get('does-not-exist'); console.log(data!.item.name); } catch (err) { if (err instanceof CompetLabError) { console.error(`${err.status} ${err.code}: ${err.message}`); // 404 project_not_found: Project not found } else { throw err; } }

code is a snake_case string matching the REST API’s error codes — api_key_invalid, project_not_found, and so on — and network or parse failures arrive as the same class with code: "network_error".

Run a free scan (start, then poll)

Three of the free tools — tech stack, trust signals, and agent adoption — are asynchronous: you start a scan and poll for the result. They don’t need a projectId — just your key. This is the one place the SDK “writes”: it kicks off a live scan of a URL you provide. Poll on a bound so a stalled scan can’t hang your job:

// Start the scan — you get back a scan ID const { data } = await cl.tools.techStack.startScan({ domain: 'example.com' }); const scanId = data!.item.id; // Poll until it finishes, giving up after a deadline let result; const deadline = Date.now() + 2 * 60_000; // stop waiting after 2 minutes while (Date.now() < deadline) { const scan = (await cl.tools.techStack.getScan(scanId)).data!.item; if (scan.status === 'completed') { result = scan.result; break; } if (scan.status === 'failed') throw new Error(scan.error?.code); await new Promise((r) => setTimeout(r, 3000)); } if (!result) throw new Error('tech-stack scan did not finish in time'); console.log(result);

The same start/get pattern applies to cl.tools.trustSignals and cl.tools.agentAdoption. The other three tools — cl.tools.sitemapVisualizer, cl.tools.aiCrawlerChecker, and cl.tools.fetchUrl — are synchronous: they return their result directly in one call, no scan ID and no polling. Scan IDs expire after 24 hours.

Next steps

  • SDK reference → — all 34 methods, grouped by resource, with parameters, return types, and the endpoint each maps to.
  • REST API → — the HTTP surface the SDK wraps, if you want to see what’s underneath.
Last updated on