Tech Stack
REST endpoints for Tech Stack. Base URL https://api.competlab.com; every request needs a CL-API-Key header (see Authentication). Responses wrap in { item } or { items }; errors in { error }.
Start an async tech-stack scan
/v1/tools/tech-stack/scansStart an asynchronous tech-stack detection scan for a public domain. Detects the frontend frameworks, hosting and CDN infrastructure, CMS, analytics and growth tools, and customer engagement services in use. Returns sub-second with item: { id, status: "queued", createdAt, expiresAt } — poll GET /v1/tools/tech-stack/scans/{scanId} until status is completed or failed. Most scans complete in seconds; heavy-render sites can take up to ~90 seconds. Result and error payloads are persisted for 24h, after which the scan auto-deletes and GET returns 404. No project required. Suitable for ad-hoc competitive lookups, integration into onboarding flows, and pre-sales discovery — and the only supported shape for MCP clients (Cursor / Claude Desktop hardcode 60-second tool-call timeouts).
Request body — application/json
| Field | Type | Description |
|---|---|---|
domain | string | Target domain to fingerprint. Accepts a bare hostname or a full URL — normalized (lowercased, scheme and path stripped) before scanning. |
Request
curl -X POST "https://api.competlab.com/v1/tools/tech-stack/scans" \
-H "CL-API-Key: YOUR_COMPETLAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"stripe.com"}'Response 200 OK
Example
{
"item": {
"id": "6638e9a1b2c3d4e5f6a7b8c9",
"status": "queued",
"createdAt": "2026-05-17T14:30:00.000Z",
"expiresAt": "2026-05-18T14:30:00.000Z"
}
}Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameters | Bad request — the payload failed validation. |
| 401 | api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scope | The CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope. |
| 429 | rate_limit_exceeded | Rate limit exceeded — retry with backoff. |
| 502 | bad_gateway | Upstream fetch failed (e.g. the target page could not be retrieved). |
Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.
{
"error": {
"code": "invalid_parameters",
"message": "Domain is required",
"status": 400
}
}Transient failures (429, 5xx) are safe to retry with exponential backoff.
Get an async tech-stack scan by ID
/v1/tools/tech-stack/scans/{scanId}Get the current state of an async tech-stack scan. Returns the scan’s lifecycle (queued / running / completed / failed) along with createdAt and expiresAt always, plus startedAt once the runner picks it up and completedAt once it reaches a terminal state. On completed the full canonical scan response is in item.result (categorized technology hits with evidence). On failed the failure details are in item.error with a machine-readable code — currently homepage_fetch_failed (upstream homepage fetch threw) or scan_failed (any other error during scan execution). Scoped to the API key’s organization — cross-tenant access, never-existed IDs, expired IDs (24h TTL), and malformed IDs all return 404 scan_not_found with an identical envelope to prevent existence-disclosure. Typically sub-second — safe to poll on a 2–5 second interval depending on your timeout budget.
Path parameters
| Name | Type | Description |
|---|---|---|
scanId | string |
Request
curl "https://api.competlab.com/v1/tools/tech-stack/scans/65a1b2c3d4e5f6a7b8c9d0e1" \
-H "CL-API-Key: YOUR_COMPETLAB_API_KEY"Response 200 OK
Example
{
"item": {
"id": "6638e9a1b2c3d4e5f6a7b8c9",
"status": "completed",
"createdAt": "2026-05-17T14:30:00.000Z",
"expiresAt": "2026-05-18T14:30:00.000Z",
"startedAt": "2026-05-17T14:30:00.412Z",
"completedAt": "2026-05-17T14:30:45.218Z",
"result": {
"domain": "stripe.com",
"finalUrl": "https://stripe.com/",
"fetchedAt": "2026-05-17T14:30:45.000Z",
"totalTechnologies": 3,
"techStack": [
{
"name": "Next.js",
"evidenceCount": 2,
"evidence": [
{
"kind": "html",
"source": "/_next/static/",
"matched": "<script src=\"/_next/static/chunks/main-app-abc123.js\""
},
{
"kind": "header",
"source": "x-powered-by",
"matched": "Next.js"
}
]
},
{
"name": "Cloudflare",
"evidenceCount": 1,
"evidence": [
{
"kind": "header",
"source": "server",
"matched": "cloudflare"
}
]
}
],
"growthStack": [
{
"name": "HubSpot",
"evidenceCount": 1,
"evidence": [
{
"kind": "html",
"source": "js.hs-scripts.com",
"matched": "<script src=\"//js.hs-scripts.com/1234567.js\""
}
]
}
],
"engagementStack": []
}
}
}Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scope | The CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope. |
| 404 | scan_not_found | No scan matches the id in the path. |
| 429 | rate_limit_exceeded | Rate limit exceeded — retry with backoff. |
Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.
{
"error": {
"code": "api_key_invalid",
"message": "Invalid API key",
"status": 401
}
}Transient failures (429, 5xx) are safe to retry with exponential backoff.