CompetLab TypeScript SDK
What it is
The SDK is the typed TypeScript path to CompetLab. It wraps the same
REST API the dashboards use, so every method maps to a real
endpoint and every response comes back as a typed object — your projects, competitors, the
five monitored dimensions, your alerts and schedules, the Strategic Briefing, and the free
scan tools. You write cl.pricing.dashboard(projectId); the SDK builds the request, sends
your key, checks the status, and hands you back a typed result.
Pick the SDK when you’re building on CompetLab in TypeScript or Node.js — a backend service, a scheduled job, an internal tool. It’s the same data as the REST API and the MCP server, aimed at a different caller: the REST API is for any language over plain HTTP, the MCP server is for AI agents that discover and call tools mid-conversation, and the SDK is for TypeScript code that wants types and autocomplete instead of raw HTTP. Under the hood all three read the same backend.
Install
npm
npm install @competlab/sdkIt’s published on npm as @competlab/sdk,
MIT-licensed, with no runtime dependencies — it uses the native fetch built into
Node.js 20+. The package ships both ESM and CommonJS builds with TypeScript declarations for
each, so both import CompetLab from '@competlab/sdk' and
const CompetLab = require('@competlab/sdk').default work, with types either way. (The client
is the default export, so CommonJS reaches it via .default.)
Quick start
import CompetLab from '@competlab/sdk';
const cl = new CompetLab({ apiKey: process.env.COMPETLAB_API_KEY! });
// See how ChatGPT, Claude, and Gemini mention your brand versus competitors
const { data } = await cl.aiVisibility.dashboard('65a1b2c3d4e5f6a7b8c9d0e1');
console.log(data!.item.summary);That’s the whole shape of it: construct once with a key, then call a method. The
Quickstart walks the first request end to end — including why
data is destructured and why the ! is there.
The surface
The client is organized into 12 resources that mirror the API. Each method is a thin, typed call over one endpoint:
| Resource | Methods | What you reach |
|---|---|---|
health | 1 | Service health check (the one call that needs no key). |
projects | 2 | List your projects; get one project’s setup and freshness. |
competitors | 2 | List a project’s competitors; get one competitor’s detail. |
aiVisibility | 4 | AI Visibility dashboard, history, one check’s detail, and provider trend. |
positioning | 3 | Positioning dashboard, history, and one run’s detail. |
pricing | 3 | Pricing dashboard, history, and one run’s detail. |
content | 4 | Content dashboard, history, run detail, and the content changelog. |
techTrust | 3 | Tech & Trust dashboard, history, and one run’s detail. |
alerts | 1 | List a project’s alerts, filtered by dimension and severity. |
schedules | 1 | List a project’s monitoring schedules. |
strategicBriefing | 1 | Pull the Strategic Briefing for a project. |
tools | 9 | The free scans — sitemap, AI-crawler, URL fetch, and start/get for the tech-stack, trust-signals, and agent-adoption scans. |
That’s 34 methods in total. Every one, with its parameters and return type, is in the SDK reference.
Authentication
Pass your CompetLab API key when you construct the client — it’s the one required option:
const cl = new CompetLab({ apiKey: 'cl_live_...' });The SDK sends that key on every request as a CL-API-Key header — the same header the REST
API expects. Keys start with cl_live_ and are 40 characters; create and manage them in your
CompetLab organization settings. Two honest notes:
- The SDK does not read environment variables for you. The
apiKeyoption is required and has no fallback — if you keep your key inCOMPETLAB_API_KEY, you pass it in yourself (process.env.COMPETLAB_API_KEY!). Nothing is read from the environment behind your back. - The only other option is
baseUrl, which defaults tohttps://api.competlab.com. You’d override it only to point at a different environment; almost no one needs to.
Responses and types
Every method returns the parsed response alongside the raw request and response objects, so
you destructure data:
const { data } = await cl.projects.list();
// data.items => ProjectListItemResponse[]Responses follow the REST API’s envelopes, and the types match:
- A single resource comes back as
{ item: ... }. - A list comes back as
{ items: [...] }. - A paginated list adds
{ items, pagination }, wherepaginationis{ page, limit, total, totalPages, hasMore }. Paging is manual — pass{ page, limit }and loop onhasMore; there’s no auto-paging iterator. - The Strategic Briefing is the one exception: it returns
{ item, meta, coverage, dimensionHealth }, and you branch onmeta.availabilitybefore readingitem.
All response types are exported from the package, so you can import and reference them
directly. One TypeScript detail worth knowing up front: methods are typed as if data
might be undefined, which is why the examples write data!. At runtime the client is
configured to throw on any non-2xx response, so on the success path data is always
present — the ! just tells the compiler what the runtime already guarantees. The
Quickstart shows the pattern in context.
Errors
Every failure throws a single typed error, CompetLabError:
import CompetLab, { CompetLabError } from '@competlab/sdk';
try {
const { data } = await cl.projects.get('does-not-exist');
} catch (err) {
if (err instanceof CompetLabError) {
console.error(err.status, err.code, err.message);
// e.g. 404 "project_not_found" "Project not found"
}
}CompetLabError carries the HTTP status (number), a snake_case code (string) matching
the REST API’s error codes, and a human-readable message. Network and parse failures throw
the same class with code: "network_error". There’s one error class, not a hierarchy — you
branch on status or code.
What it doesn’t do
The SDK is deliberately a thin, predictable client — no hidden magic between your call and the wire. That’s a design choice, not a gap: you stay in control of retries, timing, and paging rather than inheriting a framework’s opinions. Concretely:
- It doesn’t retry or time out for you. Each call is a single request. Wrap it yourself if you want retry or timeout behavior — the SDK won’t second-guess your policy.
- It doesn’t auto-paginate. Pagination is explicit via
{ page, limit }and thepaginationobject, so you decide when to fetch the next page. - No environment-variable magic, streaming, webhooks, or CLI. You pass the key explicitly, and there’s no bundled command-line tool — the SDK is a typed request layer, nothing more.
- It reads. The API surface the SDK covers is read-first. The only “writes” are the three asynchronous scan tools — tech stack, trust signals, and agent adoption — which start a live scan of a URL you give them and return an ID you poll. The other three free tools (sitemap, AI-crawler, and URL fetch) are plain reads that return inline. Nothing in the SDK changes your CompetLab projects, alerts, or settings.
Next steps
- Quickstart → — install, construct, your first call, and the async-scan poll loop, end to end.
- SDK reference → — all 34 methods, grouped by resource, with parameters, return types, and the endpoint each maps to.
FAQ
What is @competlab/sdk?
It's the official TypeScript SDK for the CompetLab REST API — a typed client you install with npm install @competlab/sdk. Instead of writing fetch calls, you construct a client with your API key and call typed methods like cl.pricing.dashboard(projectId). It covers the entire API: 34 methods across 12 resources, spanning your projects, competitors, the five monitored dimensions, alerts, schedules, the Strategic Briefing, and the free scan tools. It ships full TypeScript types, throws a typed CompetLabError on failure, has zero runtime dependencies, and runs on Node.js 20 or newer.
How do I install and authenticate?
Run npm install @competlab/sdk, then construct the client with your API key: new CompetLab({ apiKey: 'cl_live_...' }). The key is the one required option. The SDK sends it on every request as a CL-API-Key header — the same header the REST API expects. Keys start with cl_live_ and are 40 characters; you create them in your CompetLab organization settings. The SDK does not read environment variables for you, so if your key lives in COMPETLAB_API_KEY you pass it in explicitly.
How is the SDK different from the REST API and the MCP server?
Same data, different caller. The REST API is plain HTTP for any language. The MCP server is for AI agents that discover and call tools mid-conversation. The SDK is for TypeScript and Node.js code that wants types and autocomplete instead of raw HTTP — it's a thin typed layer over the same REST API, so anything you can do with one you can do with the others. Pick the surface that matches who's calling: HTTP client, AI agent, or TypeScript code.
What does a method return?
Each method returns the parsed response, which you destructure as data. Responses follow the REST API's envelopes: a single resource is { item }, a list is { items }, and a paginated list adds { items, pagination } with page, limit, total, totalPages, and hasMore. The Strategic Briefing is the exception — it returns { item, meta, coverage, dimensionHealth }, and you check meta.availability before reading item. All response types are exported from the package, so you can import and reference them directly.
Why do the examples write data! with an exclamation mark?
TypeScript types data as possibly undefined, but at runtime the client is configured to throw on any non-2xx response. So on the success path — after the call returns without throwing — data is always defined. The non-null assertion (!) tells the compiler what the runtime already guarantees. If you'd rather avoid it, you can narrow with an explicit check; the throw-on-error behavior means a returned result is always a success.
How do errors work?
Every failure throws a single typed error, CompetLabError, which you catch and inspect. It carries the HTTP status as a number, a snake_case code string that matches the REST API's error codes (like project_not_found or api_key_invalid), and a human-readable message. Network and parse failures throw the same class with code "network_error". There's no error hierarchy — one class — so you branch on status or code.
Does it retry, time out, or auto-paginate?
No — the SDK is a typed request layer, not a framework. Each call is a single request with no automatic retries and no built-in timeout, so if you need those you wrap the call yourself. Pagination is manual too: pass { page, limit } and loop on the pagination object's hasMore flag. There's no auto-paging iterator, no streaming, no webhooks, and no bundled CLI. Keeping it thin is deliberate — it's the same behavior as calling the REST API directly, just typed.
Which runtimes and module systems does it support?
It requires Node.js 20 or newer, because it uses the native fetch built into that version rather than shipping a polyfill. The package publishes both ESM and CommonJS builds with matching TypeScript declarations, so both import CompetLab from '@competlab/sdk' and const CompetLab = require('@competlab/sdk').default work, with full types either way. The client is the default export, so CommonJS reaches it through .default. It has zero runtime dependencies and is tree-shakeable.