Skip to Content

Tech & Trust

REST endpoints for Tech & Trust. 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 }.

Get latest tech & trust data

GET/v1/projects/{projectId}/tech-trust

Get the latest tech & trust profile data for all competitors in a project. Returns security headers, trust signals, technology stack, robots.txt analysis, AI access, and DNS infrastructure. Trust signals are a fixed set of 26 things we look for on a HOMEPAGE, in 5 categories — each category description names its exact membership and its denominator, so a count of 0 means none of THOSE signals was found and nothing more. Do not infer a signal a category does not list. Fields we could not measure are null, with a sibling availability object giving the reason; never read a null as zero, false or absent, and note that a measured 0 / false / empty list is reported as itself and is a real finding. Two degradations are partial and worth distinguishing: shielded response headers null the security grade and score while the HTML-derived trust and tech counts stay real, and an unreadable robots.txt withholds the AI-access verdicts — under aiAccess the assistantAccess and modelTrainingAccess keys are OMITTED rather than nulled or emptied, because an empty array would claim we evaluated every assistant and none can reach the site. aiAccess itself is ABSENT on a check that carries no AI-access information — that is neither could_not_measure nor openness, so check the key exists before branching on it. Where it is present, check aiAccess.measurement.status before reading the verdicts: measured_no_policy_found is a real result — the site publishes no robots.txt, which under the standard allows every crawler — and could_not_measure is the only status that withholds verdicts. A project with no completed run for this dimension yet answers 404 no_data_available — not a missing project and not a failed measurement, just nothing measured here so far; the project itself is fine and get_project still describes it. Distinguish it from project_not_found, which means the id is wrong.

Returns · { item }

Path parameters

NameTypeDescription
projectIdstringProject ID

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/tech-trust" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "lastUpdatedAt": "2026-03-15T10:00:00.000Z", "summary": { "competitorsChecked": 6, "competitorsSuccessful": 5, "customer": { "domain": "yoursite.com", "securityGrade": "B", "securityScore": 72, "securitySignalsAvailable": { "available": false, "reason": "site_uses_behavioral_protection" }, "trustSignalCount": 8, "techStackCount": 14, "techTrustAnalysisAvailable": { "available": false, "reason": "tech_trust_fetch_failed" } }, "topSecurityCompetitor": { "domain": "rival.com", "securityGrade": "A", "securityScore": 95 }, "topTrustCompetitor": { "domain": "other.com", "trustSignalCount": 15 }, "securityScoreGap": -23, "trustSignalGap": -7, "trustComparisonState": "compared", "comparableCompetitors": 4 }, "competitors": [ { "domain": "competitor.com", "isOwn": false, "securityHeaders": { "grade": "A", "score": 85, "hsts": true, "csp": true, "xFrameOptions": true, "xContentTypeOptions": true, "signalsAvailable": { "available": false, "reason": "site_uses_behavioral_protection" } }, "trustSignals": { "totalCount": 10, "categories": { "compliance": 3, "reviews": 4, "socialProof": 2, "certifications": 1, "disclosures": 1 } }, "technologyStack": { "tech": [ "React", "Next.js", "TypeScript" ], "growth": [ "Google Analytics", "HubSpot" ], "engagement": [ "Intercom", "Zendesk" ], "totalCount": 12, "partialDetection": { "headersAvailable": false, "reason": "site_uses_behavioral_protection" } }, "robotsTxt": { "exists": true, "totalRules": 5, "robotsTxtAvailable": { "available": false, "reason": "access_http_error_status" } }, "aiAccess": { "measurement": { "status": "measured", "measuredAt": "2026-08-13T04:12:00.000Z", "couldNotMeasureReason": "access_http_error_status", "sourcesRead": { "robotsTxt": {}, "pageMetaRobotsTag": {}, "httpResponseHeaders": {} }, "explanations": [ {} ] }, "assistantAccess": [ { "assistantName": "ChatGPT", "crawlerAccessStatus": "can_reach_site", "appliesTo": "whole_site", "decidedByCrawlers": [], "explanations": [] } ], "modelTrainingAccess": [ { "modelOperatorName": "OpenAI", "trainingAccessStatus": "may_use_content_for_training", "decidedByCrawlers": [], "explanations": [] } ], "rulesThatMayNotWork": [ { "userAgentInFile": "ChatGPT-User", "directiveInFile": "Disallow: /", "whyItMayNotWork": "operator_exempts_user_initiated_requests", "explanation": {} } ], "rulesWithUnintendedScope": [ { "userAgentInFile": "GPTBot", "kind": "escapes_restrictions_others_get", "affectedRules": [ "Disallow: /use-cases/fintech", "Disallow: /events/*" ], "explanation": {} } ], "additionalObservations": [ { "code": "training_blocked", "text": "Blocking a training crawler asks these operators not to use your content for model training. It does not affect whether AI assistants can cite you." } ], "crawlerCatalogVersion": "2026-08-13" }, "dnsInfrastructure": { "emailProvider": "Google Workspace", "dnsProvider": "Cloudflare", "emailProviderAvailable": { "available": false, "reason": "dns_lookup_failed" }, "dnsProviderAvailable": { "available": false, "reason": "dns_lookup_failed" } } } ] } }

Errors

StatusCodeMeaning
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.
404project_not_found · no_data_availableNo project matches the id in the path.

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 } }

Get tech & trust run history

GET/v1/projects/{projectId}/tech-trust/history

Get paginated history of tech & trust monitoring runs for a project. Each entry includes run timing and summary statistics. Fields we could not measure are null, with a sibling availability object giving the reason; never read a null as zero, false or absent, and note that a measured 0 / false / empty list is reported as itself and is a real finding. The two gap figures are null when there was no comparison to make — either side unmeasured, or no competitor to compare against — so a null gap is not a tie.

Returns · { items, pagination }

Path parameters

NameTypeDescription
projectIdstringProject ID

Query parameters

NameTypeRequiredDescription
pagenumberPage number (1-indexed). Default 1. Min 1.
limitnumberNumber of items per page. Default 20. Range 1100.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/tech-trust/history?page=1&limit=20" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "items": [ { "runId": "507f1f77bcf86cd799439011", "completedAt": "2026-03-15T10:00:00.000Z", "summary": { "competitorsChecked": 6, "competitorsSuccessful": 5, "customer": { "domain": "yoursite.com", "securityGrade": "B", "securityScore": 72, "securitySignalsAvailable": { "available": false, "reason": "site_uses_behavioral_protection" }, "trustSignalCount": 8, "techStackCount": 14, "techTrustAnalysisAvailable": { "available": false, "reason": "tech_trust_fetch_failed" } }, "topSecurityCompetitor": { "domain": "rival.com", "securityGrade": "A", "securityScore": 95 }, "topTrustCompetitor": { "domain": "other.com", "trustSignalCount": 15 }, "securityScoreGap": -23, "trustSignalGap": -7, "trustComparisonState": "compared", "comparableCompetitors": 4 } } ], "pagination": { "page": 1, "limit": 20, "total": 47, "totalPages": 3, "hasMore": true } }

Paginated — pass page and limit query parameters and follow pagination.hasMore to page through the full set.

Errors

StatusCodeMeaning
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.

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 } }

Get tech & trust data for a specific run

GET/v1/projects/{projectId}/tech-trust/history/{runId}

Get full tech & trust data for a specific historical monitoring run, including per-competitor security headers, trust signals, technology stack, robots.txt, and DNS infrastructure. Fields we could not measure are null, with a sibling availability object giving the reason; never read a null as zero, false or absent, and note that a measured 0 / false / empty list is reported as itself and is a real finding. A competitor whose robots.txt reads exists: false with NO availability marker genuinely publishes none, which allows all crawlers — a real finding, distinct from a null. A run that finished but produced no summary answers 404 run_not_summarized, which is a different fact from run_not_found: the run exists, it just has nothing to report. It used to answer 200 with an all-zero summary invented from the missing one.

Returns · { item }

Path parameters

NameTypeDescription
runIdstringRun ID
projectIdstringProject ID

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/tech-trust/history/507f1f77bcf86cd799439012" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "runId": "507f1f77bcf86cd799439011", "completedAt": "2026-03-15T10:00:00.000Z", "summary": { "competitorsChecked": 6, "competitorsSuccessful": 5, "customer": { "domain": "yoursite.com", "securityGrade": "B", "securityScore": 72, "securitySignalsAvailable": { "available": false, "reason": "site_uses_behavioral_protection" }, "trustSignalCount": 8, "techStackCount": 14, "techTrustAnalysisAvailable": { "available": false, "reason": "tech_trust_fetch_failed" } }, "topSecurityCompetitor": { "domain": "rival.com", "securityGrade": "A", "securityScore": 95 }, "topTrustCompetitor": { "domain": "other.com", "trustSignalCount": 15 }, "securityScoreGap": -23, "trustSignalGap": -7, "trustComparisonState": "compared", "comparableCompetitors": 4 }, "competitors": [ { "domain": "competitor.com", "isOwn": false, "securityHeaders": { "grade": "A", "score": 85, "hsts": true, "csp": true, "xFrameOptions": true, "xContentTypeOptions": true, "signalsAvailable": { "available": false, "reason": "site_uses_behavioral_protection" } }, "trustSignals": { "totalCount": 10, "categories": { "compliance": 3, "reviews": 4, "socialProof": 2, "certifications": 1, "disclosures": 1 } }, "technologyStack": { "tech": [ "React", "Next.js", "TypeScript" ], "growth": [ "Google Analytics", "HubSpot" ], "engagement": [ "Intercom", "Zendesk" ], "totalCount": 12, "partialDetection": { "headersAvailable": false, "reason": "site_uses_behavioral_protection" } }, "robotsTxt": { "exists": true, "totalRules": 5, "robotsTxtAvailable": { "available": false, "reason": "access_http_error_status" } }, "aiAccess": { "measurement": { "status": "measured", "measuredAt": "2026-08-13T04:12:00.000Z", "couldNotMeasureReason": "access_http_error_status", "sourcesRead": { "robotsTxt": {}, "pageMetaRobotsTag": {}, "httpResponseHeaders": {} }, "explanations": [ {} ] }, "assistantAccess": [ { "assistantName": "ChatGPT", "crawlerAccessStatus": "can_reach_site", "appliesTo": "whole_site", "decidedByCrawlers": [], "explanations": [] } ], "modelTrainingAccess": [ { "modelOperatorName": "OpenAI", "trainingAccessStatus": "may_use_content_for_training", "decidedByCrawlers": [], "explanations": [] } ], "rulesThatMayNotWork": [ { "userAgentInFile": "ChatGPT-User", "directiveInFile": "Disallow: /", "whyItMayNotWork": "operator_exempts_user_initiated_requests", "explanation": {} } ], "rulesWithUnintendedScope": [ { "userAgentInFile": "GPTBot", "kind": "escapes_restrictions_others_get", "affectedRules": [ "Disallow: /use-cases/fintech", "Disallow: /events/*" ], "explanation": {} } ], "additionalObservations": [ { "code": "training_blocked", "text": "Blocking a training crawler asks these operators not to use your content for model training. It does not affect whether AI assistants can cite you." } ], "crawlerCatalogVersion": "2026-08-13" }, "dnsInfrastructure": { "emailProvider": "Google Workspace", "dnsProvider": "Cloudflare", "emailProviderAvailable": { "available": false, "reason": "dns_lookup_failed" }, "dnsProviderAvailable": { "available": false, "reason": "dns_lookup_failed" } } } ] } }

Errors

StatusCodeMeaning
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.
404run_not_found · run_not_summarizedNo run matches the id in the path.

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 } }
Last updated on