SiteGauge REST API v1
Programmatic access to SiteGauge: create and manage monitors, subscribe to changes as they are detected, and pull changes, competitors, battlecards and win/loss data straight into your own tools.
https://api.sitegauge.com/api/v1application/jsonAuthentication
All API requests require a Bearer token. Generate API keys from Settings → API Keys. Keys start with vgl_. API access is included on every plan.
curl "https://api.sitegauge.com/api/v1/changes" \ -H "Authorization: Bearer vgl_your_api_key_here"
A key has full data access to your workspace. It can read and change monitors, changes, competitors, webhook subscriptions and API keys. It cannot touch billing, delete or rename the workspace, send invites, create workspaces, or change stored credentials: those need a signed-in session. Keys cannot yet be scoped down, so treat one like a password. It is shown once on creation; revoke and regenerate it from Settings if it leaks.
Rate limits
Rate limits scale with your plan: up to 60 requests per minute on Free, 120 on Standard, 300 on Pro, and 600 on Business and Enterprise. The limit is counted per workspace: every API key of one workspace shares that workspace's allowance, and keys of other workspaces calling from the same address do not count against it. Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. Exceeding your limit returns HTTP 429 with a Retry-After header in seconds; wait that long and retry.
Errors
Errors return a JSON body of the form { "error": "...", "code": "..." } with a matching HTTP status. error is a human-readable message and may change; code is stable, so branch on that.
Webhook signatures
SiteGauge signs webhook deliveries with HMAC SHA-256 so you can check they came from us and were not changed. In both delivery paths the signed string is the X-SiteGauge-Timestamp header value, a dot, and the raw request body, but the two paths differ in two details: REST-hook subscriptions send the signature as bare hex with a timestamp in seconds, while webhook alert channels send it as sha256= followed by the hex digest, with a timestamp in milliseconds.
REST-hook deliveries
Deliveries to subscriptions created with POST /hooks/subscriptions, including subscriptions made by Zapier or n8n. Each detected change arrives once per subscription as event change.created.
Headers
Verify a delivery
- Read the raw request body before parsing it as JSON.
- Compute HMAC SHA-256 of the X-SiteGauge-Timestamp value, a dot, and the raw body, keyed with the secret POST /hooks/subscriptions returned, and hex-encode it.
- Compare it with X-SiteGauge-Signature using a constant-time comparison.
- Reject the delivery if X-SiteGauge-Timestamp is more than 5 minutes (300 seconds) from your clock.
- Treat repeats of the same X-SiteGauge-Event-Id as one event, and reply with any 2xx status within 10 seconds.
Example payload
{
"event": "change.created",
"change": {
"changeId": "chg_abc123",
"severity": "HIGH",
"changeScore": 82,
"changeTypes": ["pricing", "text_change"],
"category": "pricing",
"importanceScore": null,
"aiSummary": null,
"summary": "Pricing text changed on Competitor Pricing",
"url": {
"id": "url_xyz",
"url": "https://competitor.com/pricing",
"displayName": "Competitor Pricing",
"tags": ["pricing"],
"isCompetitor": true
},
"detectedAt": "2026-05-10T09:00:00.000Z",
"viewChangeUrl": "https://sitegauge.com/dashboard/changes/chg_abc123"
}
}Example check (Node.js)
const crypto = require("crypto");
// timestamp: the X-SiteGauge-Timestamp header. rawBody: the request body exactly as received.
function signatureMatches(secret, timestamp, rawBody, signatureHeader) {
const expected = crypto.createHmac("sha256", secret).update(timestamp + "." + rawBody).digest("hex");
const given = String(signatureHeader || "").replace(/^sha256=/, "");
if (given.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
// REST hooks: the timestamp is in seconds.
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;Webhook alert channels
Deliveries to a webhook channel you add in SiteGauge (Alerts, Add channel, Webhook). Every delivery is one JSON object with subject, body (the rendered alert text), url (a link into SiteGauge) and event (the structured detail). A single alert has event.eventType set to CONTENT_CHANGE, OUTAGE, RECOVERY, SLOW_RESPONSE, SSL_EXPIRING, WHOIS_EXPIRING or THRESHOLD_BREACH, plus siteName, siteUrl, severity and occurredAt. A content change also carries event.changeId, event.changeTypes, event.summary and event.changeScore, which is a fraction from 0 to 1 (0.82 means 82 percent of the page changed). When alerts are grouped by your alert cadence, one delivery carries event.batch true, event.count, event.items (one entry per alert, each with siteName, siteUrl, eventType, severity, occurredAt and, for a content change, changeId) and event.truncated. A weekly recap carries event.weeklyRecap true. Older alert rules created through the API with a webhook destination keep their original flat body, with event change.detected and a top-level changeId. Deliveries are signed whenever the channel has a signing secret, which SiteGauge shows once when you add the channel.
Headers
Verify a delivery
- Read the raw request body before parsing it as JSON.
- Compute HMAC SHA-256 of the X-SiteGauge-Timestamp value, a dot, and the raw body, keyed with the channel's signing secret, and hex-encode it.
- Remove the sha256= prefix from X-SiteGauge-Signature, then compare using a constant-time comparison.
- Reject deliveries whose timestamp is far from your clock (5 minutes is a sensible window); this value is in milliseconds.
- Drop repeats of the same X-SiteGauge-Idempotency-Key, and reply with any 2xx status.
Example payload
{
"subject": "Competitor Pricing has changed",
"body": "We detected a change on Competitor Pricing: Pro plan increased from $49 to $59/mo",
"url": "https://sitegauge.com/dashboard/changes/chg_abc123",
"event": {
"eventType": "CONTENT_CHANGE",
"siteName": "Competitor Pricing",
"siteUrl": "https://competitor.com/pricing",
"severity": "HIGH",
"occurredAt": "2026-05-10T09:00:00.000Z",
"detail": "",
"changeId": "chg_abc123",
"changeTypes": ["pricing", "text_change"],
"changeScore": 0.82,
"summary": "Pro plan increased from $49 to $59/mo",
"ctaUrl": "https://sitegauge.com/dashboard/changes/chg_abc123",
"ctaLabel": "Track this change in SiteGauge"
}
}Example check (Node.js)
const crypto = require("crypto");
// timestamp: the X-SiteGauge-Timestamp header. rawBody: the request body exactly as received.
function signatureMatches(secret, timestamp, rawBody, signatureHeader) {
const expected = crypto.createHmac("sha256", secret).update(timestamp + "." + rawBody).digest("hex");
const given = String(signatureHeader || "").replace(/^sha256=/, "");
if (given.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
// Alert channels: the timestamp is in milliseconds.
const fresh = Math.abs(Date.now() - Number(timestamp)) <= 300000;Why two formats: the webhook alert channel and the REST-hook API were built separately, and live receivers depend on each form (the Zapier app reads the bare hex one), so both stay exactly as described here. A receiver that handles both can strip an optional sha256= prefix before comparing, and should read the timestamp unit from the path the delivery came from.
Monitors
/urlsReturns the monitors in your workspace with paging metadata. Each monitor also carries effectiveCrawlFrequency: the cadence it actually runs at on your plan (lowercase), which is slower than crawlFrequency only after a downgrade.
Query parameters
Errors
400 INVALID_FILTERThe status filter is not one of active, paused, error or pending.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/urls?status=active&pageSize=1" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10",
"url": "https://competitor.com/pricing",
"displayName": "Competitor Pricing",
"status": "ACTIVE",
"crawlFrequency": "DAILY",
"effectiveCrawlFrequency": "daily",
"workflow": "COMPETITOR",
"tags": ["pricing"],
"isCompetitor": true,
"lastCrawledAt": "2026-05-10T08:00:00Z",
"lastChangeAt": "2026-05-10T09:00:00Z",
"faviconUrl": "https://cdn.sitegauge.com/favicons/competitor.png",
"createdAt": "2026-04-01T10:00:00Z"
}
],
"meta": { "total": 38, "page": 1, "pageSize": 1, "hasMore": true }
}
// Extra monitor fields beyond those shown are also returned./urlsCreate a monitor. Only url is required. A cadence or exclusion you omit takes your workspace's crawl defaults (Settings, Crawl defaults), then the defaults below. The first check starts straight away unless the workspace has used its monthly check allowance, in which case the response also carries firstCrawlDeferred: true and a notice, and the first check waits for the next scheduled run. Returns 201.
Request body
Errors
402 TAGS_REQUIRES_UPGRADETags were sent on a plan without tags.403 FREQUENCY_NOT_ALLOWEDThe cadence is faster than your plan allows; minFrequency in the body names the fastest one it does.403 URL_QUOTA_EXCEEDEDThe workspace is at its monitor limit.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/urls" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://competitor.com/pricing", "displayName": "Competitor Pricing", "crawlFrequency": "daily"}'Example response
{
"data": {
"id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10",
"url": "https://competitor.com/pricing",
"displayName": "Competitor Pricing",
"status": "ACTIVE",
"crawlFrequency": "DAILY",
"workflow": "COMPETITOR",
"tags": [],
"createdAt": "2026-05-10T10:00:00Z"
}
}/urls/:idFetch one monitor. Adds faviconUrl, effectiveCrawlFrequency and latestCapture: the most recent stored screenshot with signed URLs valid for one hour, or null. Returns 404 if the monitor is not in your workspace.
Errors
404 NOT_FOUNDNo monitor with this id in your workspace.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": {
"id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10",
"url": "https://competitor.com/pricing",
"status": "ACTIVE",
"crawlFrequency": "HOURLY",
"effectiveCrawlFrequency": "daily",
"latestCapture": {
"capturedAt": "2026-05-10T08:00:00Z",
"screenshotUrl": "https://cdn.sitegauge.com/shots/snap_123.png",
"mobileScreenshotUrl": null
}
}
}/urls/:idUpdate a monitor. Send only the fields to change: every POST /urls field except url is accepted, with the same plan checks. Returns the updated monitor.
Request body
Errors
402 TAGS_REQUIRES_UPGRADETags were set on a plan without tags.403 FREQUENCY_NOT_ALLOWEDThe cadence is faster than your plan allows.404 NOT_FOUNDThe monitor, folderId or competitorId is not in your workspace.
Example request
curl -X PATCH "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"displayName": "Competitor pricing page"}'Example response
{ "data": { "id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10", "displayName": "Competitor pricing page" } }/urls/:idMove a monitor to trash. It stops being checked straight away and can be brought back from trash in SiteGauge while it is still there. Returns 204 No Content.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content
/urls/:id/pausePause checks on a monitor. Returns the updated monitor.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10/pause" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{ "data": { "id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10", "status": "PAUSED" } }/urls/:id/resumeResume a paused monitor. Returns the updated monitor, or 403 URL_QUOTA_EXCEEDED when the workspace is already at its monitor limit.
Errors
403 URL_QUOTA_EXCEEDEDResuming would take the workspace over its monitor limit.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10/resume" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{ "data": { "id": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10", "status": "ACTIVE" } }/urls/:id/crawlQueue a check of a monitor now, outside its schedule. Returns 202 Accepted with the queued job id; the result arrives as a change (if the page changed) like any scheduled check. A manual check counts against the workspace's monthly crawl allowance.
Errors
404 NOT_FOUNDNo monitor with this id in your workspace.429 CRAWL_BUDGET_EXCEEDEDThe workspace has used its monthly crawl allowance.429 CRAWL_COOLDOWNThe same monitor was checked by hand less than a minute ago; the error message says how many seconds to wait.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10/crawl" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{ "data": { "jobId": "crawl-3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10-1715331600000" } }Changes
/changesReturns a paginated feed of detected changes across your workspace, newest first. Dismissed changes are hidden by default. Each item is the full change record plus its parent monitor, the resulting snapshot, and a signed screenshot URL (extra change fields beyond those shown are also returned).
Query parameters
Example request
curl -X GET "https://api.sitegauge.com/api/v1/changes?significant=true&pageSize=5" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "chg_abc123",
"urlId": "url_xyz",
"severity": "HIGH",
"isSignificant": true,
"changeCategory": "pricing",
"changeScore": 82,
"importanceScore": 8,
"aiSummary": "Pricing page updated: Pro plan increased from $49 to $59/mo",
"changeTypes": ["pricing", "text_change"],
"isDismissed": false,
"createdAt": "2026-05-10T09:00:00Z",
"url": {
"id": "url_xyz",
"url": "https://competitor.com/pricing",
"displayName": "Competitor Pricing",
"tags": ["pricing"],
"isCompetitor": true,
"faviconUrl": "https://cdn.sitegauge.com/favicons/acme.png"
},
"toSnapshot": {
"id": "snap_123",
"pageTitle": "Pricing - Competitor",
"metaDescription": "Simple, transparent pricing",
"statusCode": 200,
"createdAt": "2026-05-10T09:00:00Z"
},
"screenshotUrl": "https://cdn.sitegauge.com/shots/chg_abc123.png"
}
],
"meta": { "total": 142, "page": 1, "pageSize": 20, "hasMore": true }
}/changes/:idFetch one change with its monitor, both snapshots (fromSnapshot and toSnapshot), the SEO fields before and after (seoBefore, seoAfter), and signed screenshot URLs valid for one hour (screenshotSignedUrl, fromScreenshotSignedUrl, and the mobile variants when they were captured). Links such as diffSignedUrl, boxesUrl, fromHtmlUrl, toHtmlUrl and the *ProxyUrl fields are paths relative to the base URL (https://api.sitegauge.com/api/v1), so prefix them with it and send your API key. Returns 404 if the change is not in your workspace.
Errors
404 NOT_FOUNDNo change with this id in your workspace.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/changes/chg_abc123" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": {
"id": "chg_abc123",
"severity": "HIGH",
"aiSummary": "Pricing page updated: Pro plan increased from $49 to $59/mo",
"url": { "id": "url_xyz", "url": "https://competitor.com/pricing", "faviconUrl": null },
"seoBefore": { "pageTitle": "Pricing - Competitor" },
"seoAfter": { "pageTitle": "Plans and pricing - Competitor" },
"screenshotSignedUrl": "https://cdn.sitegauge.com/shots/snap_124.png",
"fromScreenshotSignedUrl": "https://cdn.sitegauge.com/shots/snap_123.png"
}
}/urls/:urlId/changesThe change history of one monitor, newest first, with paging metadata. Dismissed changes are hidden unless dismissed=true. A monitor in trash keeps its history readable here.
Query parameters
Errors
404 NOT_FOUNDNo monitor with this id in your workspace.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/urls/3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10/changes?pageSize=1" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{ "id": "chg_abc123", "severity": "HIGH", "aiSummary": "Pro plan increased from $49 to $59/mo", "createdAt": "2026-05-10T09:00:00Z" }
],
"meta": { "total": 12, "page": 1, "pageSize": 1, "hasMore": true }
}/changes/:id/dismissDismiss a change, which hides it from the default change feed. Returns success.
Errors
404 NOT_FOUNDNo change with this id in your workspace.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/changes/chg_abc123/dismiss" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{ "data": { "success": true } }Webhooks (REST hooks)
/hooks/subscriptionsSubscribe an https endpoint to change.created events: every change detected in your workspace is POSTed to targetUrl as signed JSON, optionally narrowed to one monitor or a minimum severity. Only an owner or admin can manage subscriptions (an API key acts with owner authority). This response is the only time the signing secret is returned, so store it to verify deliveries (see Webhook signatures). A workspace can have 25 active subscriptions, and an endpoint that keeps failing is disabled automatically (disabledAt is set). Returns 201.
Request body
Errors
400 INVALID_TARGET_URLThe target URL is not https, or cannot receive deliveries.400 INVALID_FILTERfilters.urlId is not a monitor in this workspace.403 FORBIDDENOnly an owner or admin can manage webhook subscriptions.409 SUBSCRIPTION_LIMIT_REACHEDThe workspace already has 25 active subscriptions.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/hooks/subscriptions" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"targetUrl": "https://hooks.example.com/sitegauge", "filters": {"minSeverity": "HIGH"}}'Example response
{
"data": {
"id": "8d0f4a52-2c1e-4b7a-9f63-5e2d1c0b9a87",
"secret": "9b2f0c7e5d1a4f3b8c6e2d9a7b1c4e5f9b2f0c7e5d1a4f3b8c6e2d9a7b1c4e5f",
"targetUrl": "https://hooks.example.com/sitegauge",
"event": "change.created"
}
}/hooks/subscriptionsList your subscriptions, newest first. The signing secret is never returned, and targetUrl is masked to its scheme, host and first path segment because hook URLs often carry a token.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/hooks/subscriptions" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "8d0f4a52-2c1e-4b7a-9f63-5e2d1c0b9a87",
"targetUrl": "https://hooks.example.com/sitegauge",
"event": "change.created",
"filters": { "minSeverity": "HIGH" },
"createdAt": "2026-05-10T10:00:00Z",
"lastDeliveredAt": "2026-05-11T08:02:11Z",
"failCount": 0,
"disabledAt": null
}
]
}/hooks/subscriptions/:idDelete a subscription; deliveries to it stop. Owner or admin only. Returns 204 No Content, or 404 if the id is not in your workspace.
Errors
403 FORBIDDENOnly an owner or admin can manage webhook subscriptions.404 NOT_FOUNDNo subscription with this id in your workspace.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/hooks/subscriptions/8d0f4a52-2c1e-4b7a-9f63-5e2d1c0b9a87" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content
RSS feed
/feeds/rssYour workspace's change feed as RSS 2.0: the newest 50 changes that are not dismissed. Copy the feed link, token included, from the RSS channel in SiteGauge. The token is the whole credential, so no API key is sent, and regenerating the link in SiteGauge revokes the old token. Returns 401 without a token and 403 for an unknown or revoked one.
Query parameters
Errors
401 UNAUTHORIZEDNo token was sent.403 FORBIDDENThe token is unknown or has been revoked.
Example request
curl "https://api.sitegauge.com/api/v1/feeds/rss?token=your_feed_token"
Example response
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>SiteGauge changes: Your workspace</title>
<item>
<title>Competitor Pricing: pricing change</title>
<pubDate>Sun, 10 May 2026 09:00:00 GMT</pubDate>
</item>
</channel>
</rss>Evidence exports
/evidence/exportBuild a signed evidence export for one monitor's snapshots (urlId) or a list of snapshots (snapshotIds), oldest first, up to 1000. The response carries the manifest, its SHA-256 (manifestSha256), the signature and verification steps; a downloadable bundle is prepared alongside it (see the exports list). Business and Enterprise plans only.
Request body
Errors
400 BAD_REQUESTNeither urlId nor snapshotIds was sent.403 PLAN_UPGRADE_REQUIREDEvidence export is available on the Business and Enterprise plans.404 NOT_FOUNDNo matching snapshots in your workspace.503 EVIDENCE_NOT_CONFIGUREDEvidence signing is not available on this deployment.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/evidence/export" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"urlId": "3f1c2b9e-8a41-4d0e-9c3f-0b6a2d9e7f10"}'Example response
{
"data": {
"manifestSha256": "5d41402abc4b2a76b9719d911017c592ae5f2d7c1a8b3e4f6a7c9d0e1f2a3b4c",
"manifest": { "snapshots": ["..."] }
}
}
// Also returned: the export id, the signature and the verification steps./evidence/exportsList your last 100 evidence exports, newest first. hasBundle says whether a downloadable bundle exists.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/evidence/exports" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "c7a1e0f2-3b4d-4e5f-8a9b-0c1d2e3f4a5b",
"snapshotIds": ["0b6a2d9e-7f10-4d0e-9c3f-3f1c2b9e8a41"],
"manifestSha256": "5d41402abc4b2a76b9719d911017c592ae5f2d7c1a8b3e4f6a7c9d0e1f2a3b4c",
"sigAlgo": "ed25519",
"signedAt": "2026-05-10T10:00:00Z",
"requestedBy": "8f14e45f-ceea-467a-9e0f-1c2d3b4a5e6f",
"hasBundle": true
}
]
}/evidence/exports/:id/downloadGet a download link for an export's bundle, valid for one hour. Business and Enterprise plans only. Returns 409 NO_BUNDLE when the export has no bundle.
Errors
403 PLAN_UPGRADE_REQUIREDEvidence export is available on the Business and Enterprise plans.404 NOT_FOUNDNo export with this id in your workspace.409 NO_BUNDLEThis export has no downloadable bundle.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/evidence/exports/c7a1e0f2-3b4d-4e5f-8a9b-0c1d2e3f4a5b/download" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{ "data": { "downloadUrl": "https://cdn.sitegauge.com/evidence/c7a1e0f2.zip" } }/evidence/public-keyThe Ed25519 public key that verifies export signatures without trusting or contacting SiteGauge. No API key needed. Returns 404 when Ed25519 signing is not enabled.
Example request
curl "https://api.sitegauge.com/api/v1/evidence/public-key"
Example response
{ "data": { "algo": "ed25519", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" } }/evidence/verifyCheck an export's manifest signature. We recompute the SHA-256 of the manifest you send and verify the Ed25519 signature against our public key. This checks the manifest only: to confirm the bundle's files are unaltered, compare each file's SHA-256 with the hash the manifest lists for it. No API key needed.
Request body
Example request
curl -X POST "https://api.sitegauge.com/api/v1/evidence/verify" \
-H "Content-Type: application/json" \
-d '{"manifest": "{...}", "signature": "base64signature"}'Example response
{ "data": { "valid": true, "algo": "ed25519", "manifestSha256": "5d41402abc4b2a76b9719d911017c592ae5f2d7c1a8b3e4f6a7c9d0e1f2a3b4c" } }Competitors
/competitorsReturns every competitor in your workspace with its AI profile (if one has been generated) and its tracked pages. Not paginated.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/competitors" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "cmp_abc",
"name": "Acme Corp",
"domain": "acme.com",
"description": "Enterprise workflow automation",
"logoUrl": null,
"aiProfile": {
"tagline": "Enterprise workflow automation",
"industry": "SaaS",
"pricingModel": "per-seat subscription",
"targetCustomer": "Mid-market ops teams"
},
"aiProfileGeneratedAt": "2026-02-01T00:00:00Z",
"uptimeEnabled": false,
"createdAt": "2026-01-15T00:00:00Z",
"updatedAt": "2026-02-01T00:00:00Z",
"urls": [
{
"id": "url_1",
"url": "https://acme.com/pricing",
"displayName": "Acme Pricing",
"workflow": "COMPETITOR",
"status": "ACTIVE",
"lastCrawledAt": "2026-05-10T08:00:00Z",
"lastChangeAt": "2026-05-10T09:00:00Z"
}
]
}
]
}
// aiProfile is null until you run generate-profile; its keys are AI-written and vary./competitorsAdd a competitor. Returns 201 with the new competitor, or 409 if the domain is already tracked in your workspace.
Request body
Errors
409 CONFLICTA competitor with this domain already exists in your workspace.
Example request
curl -X POST "https://api.sitegauge.com/api/v1/competitors" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "domain": "acme.com"}'Example response
{
"data": {
"id": "cmp_new",
"name": "Acme Corp",
"domain": "acme.com",
"description": null,
"logoUrl": null,
"aiProfile": null,
"createdAt": "2026-05-10T10:00:00Z"
}
}/competitors/:idFetch a single competitor, including all of its tracked pages.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/competitors/cmp_abc" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": {
"id": "cmp_abc",
"name": "Acme Corp",
"domain": "acme.com",
"aiProfile": { "tagline": "Enterprise workflow automation" },
"urls": [
{ "id": "url_1", "url": "https://acme.com/pricing", "status": "ACTIVE", "crawlFrequency": "DAILY" }
]
}
}/competitors/:idUpdate a competitor's name, description or logo. The domain cannot be changed once set.
Request body
Example request
curl -X PATCH "https://api.sitegauge.com/api/v1/competitors/cmp_abc" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"description": "Enterprise workflow automation, US-based"}'Example response
{ "data": { "success": true } }/competitors/:idDelete a competitor. Its tracked pages are unlinked from the competitor, not deleted. Returns 204 No Content.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/competitors/cmp_abc" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content
/competitors/:id/askAsk a natural-language question about a competitor. The AI answers from their recent significant changes. Requires a Pro, Business or Enterprise plan (402 otherwise).
Request body
Errors
402 PAYMENT_REQUIREDYour plan does not include AI features (Pro, Business or Enterprise).
Example request
curl -X POST "https://api.sitegauge.com/api/v1/competitors/cmp_abc/ask" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"question": "Did they raise prices recently?"}'Example response
{
"data": {
"answer": "Yes - their pricing page changed 12 days ago. The Pro plan increased from $49 to $59/mo and they removed the legacy Starter tier."
}
}/competitors/:id/generate-profileGenerate or refresh a competitor's AI profile from their site and recent changes. Requires a Pro, Business or Enterprise plan (402 otherwise). The returned profile keys are AI-written and vary.
Errors
402 PAYMENT_REQUIREDYour plan does not include AI features (Pro, Business or Enterprise).
Example request
curl -X POST "https://api.sitegauge.com/api/v1/competitors/cmp_abc/generate-profile" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": {
"profile": {
"tagline": "Enterprise workflow automation",
"industry": "SaaS",
"targetCustomer": "Mid-market operations teams",
"keyProducts": ["Workflow Builder", "Analytics"],
"pricingModel": "per-seat subscription",
"strengthSummary": "Deep integrations and enterprise controls."
}
}
}Battlecards
/battlecardsReturns your battlecards, newest first, each with its competitor and creator. `sections` follows the fixed battlecard schema described under PUT /battlecards/:id (positioning, ourStrengths, theirWeaknesses, commonObjections, useCases and talkingPoints). `generatedBy` is the id of the user who created the card (the same user in `creator`).
Query parameters
Example request
curl -X GET "https://api.sitegauge.com/api/v1/battlecards?competitorId=cmp_abc" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "bc_xyz",
"competitorId": "cmp_abc",
"title": "Acme Corp Battlecard",
"isPublished": true,
"sections": {
"positioning": "We win on ease-of-use and faster onboarding.",
"ourStrengths": ["No per-seat pricing", "SOC 2 certified"],
"theirWeaknesses": ["Complex setup", "No free trial"],
"talkingPoints": ["Lead with time-to-value"]
},
"generatedBy": "8f14e45f-ceea-467a-9e0f-1c2d3b4a5e6f",
"createdAt": "2026-05-01T00:00:00Z",
"updatedAt": "2026-05-09T08:00:00Z",
"competitor": { "id": "cmp_abc", "name": "Acme Corp", "domain": "acme.com" },
"creator": { "fullName": "Jordan Lee", "email": "jordan@yourco.com" }
}
]
}/battlecards/:idFetch a single battlecard, including the competitor's AI profile.
Example request
curl -X GET "https://api.sitegauge.com/api/v1/battlecards/bc_xyz" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": {
"id": "bc_xyz",
"competitorId": "cmp_abc",
"title": "Acme Corp Battlecard",
"isPublished": true,
"sections": { "positioning": "..." },
"competitor": { "id": "cmp_abc", "name": "Acme Corp", "domain": "acme.com", "aiProfile": { "industry": "SaaS" } },
"creator": { "fullName": "Jordan Lee" }
}
}/battlecards/generateAI-generates a battlecard for a competitor from their profile and recent changes. Requires a Pro, Business or Enterprise plan (402 with code AI_REQUIRES_UPGRADE otherwise). Each competitor can be generated once every 10 minutes: a repeat inside that window returns 429 with code BATTLECARD_COOLDOWN. The title is set automatically ("<Competitor> Battlecard"). Returns 201 with the created record (no nested relations).
Request body
Example request
curl -X POST "https://api.sitegauge.com/api/v1/battlecards/generate" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"competitorId": "cmp_abc"}'Example response
{
"data": {
"id": "bc_new",
"competitorId": "cmp_abc",
"title": "Acme Corp Battlecard",
"sections": { "positioning": "...", "ourStrengths": ["..."] },
"isPublished": false,
"generatedBy": "8f14e45f-ceea-467a-9e0f-1c2d3b4a5e6f",
"createdAt": "2026-05-10T10:00:00Z",
"updatedAt": "2026-05-10T10:00:00Z"
}
}/battlecards/:idUpdate a battlecard's title, sections, or published state. `sections` replaces the whole sections object and must follow the battlecard schema: positioning, ourStrengths and theirWeaknesses are required whenever sections is sent; commonObjections, useCases and talkingPoints are optional and default to empty lists when omitted. Keys outside the schema are dropped, and a value over its length cap returns 400.
Request body
Example request
curl -X PUT "https://api.sitegauge.com/api/v1/battlecards/bc_xyz" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"isPublished": true}'Example response
{
"data": { "id": "bc_xyz", "title": "Acme Corp Battlecard", "isPublished": true, "sections": { "positioning": "..." } }
}/battlecards/:idDelete a battlecard. Returns 204 No Content.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/battlecards/bc_xyz" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content
Win / Loss
/win-lossReturns win/loss records (newest first) plus aggregate stats. Note: `stats` is always computed across your whole workspace and ignores the competitorId / outcome filters, which apply only to `data` and `total`.
Query parameters
Example request
curl -X GET "https://api.sitegauge.com/api/v1/win-loss?outcome=WIN" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "wl_abc",
"competitorId": "cmp_abc",
"outcome": "WIN",
"opportunityName": "ACME vs Us - Q2 Enterprise Deal",
"dealSizeUsd": 48000,
"closeDate": "2026-04-30T00:00:00Z",
"ourStrengths": ["Better support", "Easier onboarding"],
"theirStrengths": ["Lower headline price"],
"reasonNotes": "Won on support SLA.",
"crmId": "0061t00000xyz",
"createdAt": "2026-05-01T00:00:00Z",
"competitor": { "id": "cmp_abc", "name": "Acme Corp", "domain": "acme.com" },
"creator": { "fullName": "Jordan Lee", "email": "jordan@yourco.com" }
}
],
"total": 17,
"stats": {
"total": 17, "wins": 12, "losses": 5, "draws": 0, "noDecision": 0,
"avgDealSize": 41000,
"byCompetitor": [
{ "competitorId": "cmp_abc", "competitorName": "Acme Corp", "wins": 8, "losses": 2, "total": 10 }
]
}
}/win-lossLog a win/loss record. `closeDate` must be a full ISO 8601 datetime. Returns 201.
Request body
Example request
curl -X POST "https://api.sitegauge.com/api/v1/win-loss" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"competitorId": "cmp_abc", "outcome": "WIN", "closeDate": "2026-04-30T00:00:00Z", "dealSizeUsd": 48000}'Example response
{
"data": {
"id": "wl_new",
"competitorId": "cmp_abc",
"outcome": "WIN",
"dealSizeUsd": 48000,
"closeDate": "2026-04-30T00:00:00Z",
"createdAt": "2026-05-10T10:00:00Z",
"competitor": { "id": "cmp_abc", "name": "Acme Corp" }
}
}/win-loss/:idUpdate a win/loss record. The competitor cannot be reassigned.
Request body
Example request
curl -X PUT "https://api.sitegauge.com/api/v1/win-loss/wl_abc" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"reasonNotes": "Confirmed: won on support SLA."}'Example response
{ "data": { "id": "wl_abc", "outcome": "WIN", "reasonNotes": "Confirmed: won on support SLA." } }/win-loss/:idDelete a win/loss record. Returns 204 No Content.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/win-loss/wl_abc" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content
Saved Intel
/bookmarksReturns the changes you (the key's owner) have bookmarked, newest first. Bookmarks are per-user within a workspace.
Query parameters
Example request
curl -X GET "https://api.sitegauge.com/api/v1/bookmarks" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
{
"data": [
{
"id": "bk_abc",
"changeId": "chg_abc123",
"note": "Follow up with pricing team",
"createdAt": "2026-05-08T12:00:00Z",
"change": {
"id": "chg_abc123",
"severity": "HIGH",
"isSignificant": true,
"changeCategory": "pricing",
"aiSummary": "Pricing page updated...",
"changeTypes": ["pricing"],
"createdAt": "2026-05-08T09:00:00Z",
"url": {
"url": "https://competitor.com/pricing",
"displayName": "Competitor Pricing",
"competitor": { "name": "Acme Corp" }
}
}
}
],
"total": 7
}/bookmarksBookmark a change. This is an upsert: calling it again for the same change updates the note (posting with no note clears it). Returns 201 with the flat bookmark record.
Request body
Example request
curl -X POST "https://api.sitegauge.com/api/v1/bookmarks" \
-H "Authorization: Bearer vgl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"changeId": "chg_abc123", "note": "Follow up with pricing team"}'Example response
{
"data": { "id": "bk_abc", "changeId": "chg_abc123", "note": "Follow up with pricing team", "createdAt": "2026-05-08T12:00:00Z" }
}/bookmarks/:changeIdRemove a bookmark by change id. Idempotent - deleting a change you have not bookmarked still returns 204.
Example request
curl -X DELETE "https://api.sitegauge.com/api/v1/bookmarks/chg_abc123" \ -H "Authorization: Bearer vgl_your_api_key"
Example response
HTTP/1.1 204 No Content