Skip to Content

Content

REST endpoints for Content. 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 content intelligence data

GET/v1/projects/{projectId}/content

Get the latest content intelligence data for all competitors in a project. Returns URL counts, content categorization, strategic URL analysis, and competitive gap assessment. Use this to understand the content landscape across competitors. 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 four gap lists carry a distinction worth respecting: a null list means no comparison ran, while an EMPTY list means we compared and found none — reading the first as the second reports a verdict about a comparison that never happened. 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/content" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "lastUpdatedAt": "2026-03-15T10:00:00.000Z", "summary": { "sitemapsChecked": 8, "sitemapsSuccessful": 7, "customer": { "domain": "yoursite.com", "totalUrls": 350, "strategicUrls": 280, "categorizedCounts": { "blog": 120, "docs": 80, "tools": 5, "landing": 30, "legal": 10, "caseStudies": 0, "comparison": 3, "integrations": 25, "changelog": 15, "webinars": 2, "other": 60 }, "contentAnalysisAvailable": { "available": false, "reason": "content_fetch_failed" } }, "topCompetitor": { "domain": "rival.com", "strategicUrls": 420 }, "strategicUrlGap": -140, "overallRank": 3, "comparableCompetitors": 4, "criticalGaps": [ { "category": "caseStudies", "categoryLabel": "Case Studies", "competitorsWithContent": 3, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Create 3-5 case studies showcasing customer success stories" } ], "significantGaps": [ { "category": "blog", "categoryLabel": "Blog Posts", "customerCount": 5, "competitorAverage": 25, "competitorMax": 42, "gapPercentage": -80, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Increase blog output to at least match competitor average of 25 posts" } ], "advantages": [ { "category": "docs", "categoryLabel": "Documentation", "customerCount": 120, "competitorMax": 45, "leadAmount": 75 } ], "onTrack": [ { "category": "landing", "categoryLabel": "Landing Pages", "customerCount": 30, "competitorMax": 35, "differencePercent": -14 } ], "totalCriticalGaps": 2, "totalSignificantGaps": 1, "totalAdvantages": 3, "totalOnTrack": 4 }, "competitors": [ { "domain": "competitor.com", "isOwn": false, "totalUrls": 450, "strategicUrls": 380, "categorizedCounts": { "blog": 150, "docs": 100, "tools": 10, "landing": 10, "caseStudies": 10, "comparison": 10, "integrations": 10, "changelog": 10, "webinars": 10, "legal": 10, "programmatic": 27996, "other": 65 }, "programmaticExampleUrls": [ "https://competitor.com/ncm/01012100", "https://competitor.com/cnae/0119-9-01", "https://competitor.com/cfop/1101" ], "sitemapCount": 2, "contentDataAvailable": { "available": false, "reason": "no_sitemap_published" } } ] } }

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 content intelligence run history

GET/v1/projects/{projectId}/content/history

Get paginated history of content intelligence monitoring runs for a project. Each entry includes run timing and summary statistics with gap analysis. 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.

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/content/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": { "sitemapsChecked": 8, "sitemapsSuccessful": 7, "customer": { "domain": "yoursite.com", "totalUrls": 350, "strategicUrls": 280, "categorizedCounts": { "blog": 120, "docs": 80, "tools": 5, "landing": 30, "legal": 10, "caseStudies": 0, "comparison": 3, "integrations": 25, "changelog": 15, "webinars": 2, "other": 60 }, "contentAnalysisAvailable": { "available": false, "reason": "content_fetch_failed" } }, "topCompetitor": { "domain": "rival.com", "strategicUrls": 420 }, "strategicUrlGap": -140, "overallRank": 3, "comparableCompetitors": 4, "criticalGaps": [ { "category": "caseStudies", "categoryLabel": "Case Studies", "competitorsWithContent": 3, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Create 3-5 case studies showcasing customer success stories" } ], "significantGaps": [ { "category": "blog", "categoryLabel": "Blog Posts", "customerCount": 5, "competitorAverage": 25, "competitorMax": 42, "gapPercentage": -80, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Increase blog output to at least match competitor average of 25 posts" } ], "advantages": [ { "category": "docs", "categoryLabel": "Documentation", "customerCount": 120, "competitorMax": 45, "leadAmount": 75 } ], "onTrack": [ { "category": "landing", "categoryLabel": "Landing Pages", "customerCount": 30, "competitorMax": 35, "differencePercent": -14 } ], "totalCriticalGaps": 2, "totalSignificantGaps": 1, "totalAdvantages": 3, "totalOnTrack": 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 content intelligence data for a specific run

GET/v1/projects/{projectId}/content/history/{runId}

Get full content intelligence data for a specific historical monitoring run, including per-competitor URL counts, content categorization, and strategic URL metrics. 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 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/content/history/507f1f77bcf86cd799439012" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "runId": "507f1f77bcf86cd799439011", "completedAt": "2026-03-15T10:00:00.000Z", "summary": { "sitemapsChecked": 8, "sitemapsSuccessful": 7, "customer": { "domain": "yoursite.com", "totalUrls": 350, "strategicUrls": 280, "categorizedCounts": { "blog": 120, "docs": 80, "tools": 5, "landing": 30, "legal": 10, "caseStudies": 0, "comparison": 3, "integrations": 25, "changelog": 15, "webinars": 2, "other": 60 }, "contentAnalysisAvailable": { "available": false, "reason": "content_fetch_failed" } }, "topCompetitor": { "domain": "rival.com", "strategicUrls": 420 }, "strategicUrlGap": -140, "overallRank": 3, "comparableCompetitors": 4, "criticalGaps": [ { "category": "caseStudies", "categoryLabel": "Case Studies", "competitorsWithContent": 3, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Create 3-5 case studies showcasing customer success stories" } ], "significantGaps": [ { "category": "blog", "categoryLabel": "Blog Posts", "customerCount": 5, "competitorAverage": 25, "competitorMax": 42, "gapPercentage": -80, "leader": { "domain": "competitor.com", "count": 42, "exampleUrls": [ "https://competitor.com/blog/post-1", "https://competitor.com/blog/post-2" ] }, "actionHint": "Increase blog output to at least match competitor average of 25 posts" } ], "advantages": [ { "category": "docs", "categoryLabel": "Documentation", "customerCount": 120, "competitorMax": 45, "leadAmount": 75 } ], "onTrack": [ { "category": "landing", "categoryLabel": "Landing Pages", "customerCount": 30, "competitorMax": 35, "differencePercent": -14 } ], "totalCriticalGaps": 2, "totalSignificantGaps": 1, "totalAdvantages": 3, "totalOnTrack": 4 }, "competitors": [ { "domain": "competitor.com", "isOwn": false, "totalUrls": 450, "strategicUrls": 380, "categorizedCounts": { "blog": 150, "docs": 100, "tools": 10, "landing": 10, "caseStudies": 10, "comparison": 10, "integrations": 10, "changelog": 10, "webinars": 10, "legal": 10, "programmatic": 27996, "other": 65 }, "programmaticExampleUrls": [ "https://competitor.com/ncm/01012100", "https://competitor.com/cnae/0119-9-01", "https://competitor.com/cfop/1101" ], "sitemapCount": 2, "contentDataAvailable": { "available": false, "reason": "no_sitemap_published" } } ] } }

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

Get content changelog

GET/v1/projects/{projectId}/content/changelog

Get paginated changelog of detected URL changes across competitor sitemaps. Each item shows numeric counts per category plus up to 3 sample URLs per category by default — safe for any token budget. Supports filtering by competitorId and category query parameters. Pass allUrlsPerCategory=true for the full URL list per category (warning: high-activity competitors can produce large responses; combine with filters to scope, and check the truncated flag in the response — when true, the byte cap fired and items/URLs were trimmed).

Returns · { items, pagination, truncated }

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.
competitorIdstringFilter by competitor ID
categorystringFilter by content category (blog, docs, tools, landing, caseStudies, comparison, integrations, changelog, webinars, legal, programmatic, other). Changelog rows carry the same category the content dashboard reports for the same URL, decided over the competitor’s whole sitemap rather than over the changed URLs alone — so a page added into a templated catalog filters under programmatic, not other. Values: blog, docs, tools, landing, caseStudies, comparison, integrations, changelog, webinars, legal, programmatic, other.
allUrlsPerCategorybooleanBy default each item returns up to 3 sample URLs per category. Set true to return the full URL list per category. High-activity competitors can produce large responses — combine with category and competitorId filters to scope. Subject to an internal byte cap; check truncated in the response.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/content/changelog?page=1&limit=20&competitorId=507f1f77bcf86cd799439011&category=blog&allUrlsPerCategory=false" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "items": [ { "competitorDomain": "competitor.com", "sitemapUrl": "https://competitor.com/sitemap.xml", "checkedAt": "2026-03-15T10:00:00.000Z", "totalAdded": 5, "totalRemoved": 2, "addedByCategory": { "blog": [ "https://competitor.com/blog/new-post" ], "docs": [ "https://competitor.com/docs/new-guide" ], "programmatic": [ "https://competitor.com/ncm/01012100" ] }, "removedByCategory": { "landing": [ "https://competitor.com/old-landing" ] }, "addedCounts": { "blog": 1, "docs": 1 }, "removedCounts": { "landing": 1 } } ], "pagination": { "page": 1, "limit": 20, "total": 47, "totalPages": 3, "hasMore": true }, "truncated": false }

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