Skip to Content
DevelopersSDKOverview

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 install @competlab/sdk

It’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:

ResourceMethodsWhat you reach
health1Service health check (the one call that needs no key).
projects2List your projects; get one project’s setup and freshness.
competitors2List a project’s competitors; get one competitor’s detail.
aiVisibility4AI Visibility dashboard, history, one check’s detail, and provider trend.
positioning3Positioning dashboard, history, and one run’s detail.
pricing3Pricing dashboard, history, and one run’s detail.
content4Content dashboard, history, run detail, and the content changelog.
techTrust3Tech & Trust dashboard, history, and one run’s detail.
alerts1List a project’s alerts, filtered by dimension and severity.
schedules1List a project’s monitoring schedules.
strategicBriefing1Pull the Strategic Briefing for a project.
tools9The 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 apiKey option is required and has no fallback — if you keep your key in COMPETLAB_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 to https://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 }, where pagination is { page, limit, total, totalPages, hasMore }. Paging is manual — pass { page, limit } and loop on hasMore; there’s no auto-paging iterator.
  • The Strategic Briefing is the one exception: it returns { item, meta, coverage, dimensionHealth }, and you branch on meta.availability before reading item.

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 the pagination object, 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.

Last updated on