// Developer Documentation
Sourced comparison facts, fact-checked pages, proposed corrections and drafted vs pages — as JSON, so you can build comparison pages on whatever stack you use.
Who can use it: every paid plan, and RivalCheck monitoring customers while subscribed. The same key works with the MCP server.
RivalCheck fact-checks your published comparison, “vs” and alternatives pages against each rival's own website, proposes corrected copy, and drafts the comparison pages you're missing. This API exposes all of it: every sourced fact (with its source URL and the date it was checked), your pages and their findings, proposed corrections you can accept or reject, missing-page gaps, Markdown drafts, and a hash-chained audit log. Use it from a build script, a CMS sync, or CI — or connect your AI coding tool through the MCP server, which calls the same operations.
It's a JSON REST API. Every request needs a Bearer token. Dates are ISO 8601. The base URL is https://rivalcheck.com/api/v1.
1. Get your API key
Go to Settings and create an API key. Keys begin with rc_live_ and are scoped to your organisation. You can revoke and regenerate keys at any time.
2. Make your first request
Pull every sourced fact about one rival:
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/facts?company=typeform"
3. Build from there
List your pages and their findings, pull proposed corrections, or request a Markdown draft for a rival you don't cover yet — every endpoint is documented below. Working in Claude Code, Cursor or VS Code? Connect the MCP server with the same key.
All requests to the RivalCheck competitor monitoring API must include a valid API key in the Authorization header using the Bearer scheme.
Authorization: Bearer rc_live_abc123def456ghi789...
rc_live_. There are no sandbox/test keys; use a staging organisation for development.401 Unauthorized immediately.Security tip: If you suspect a key has been compromised, revoke it immediately and generate a new one. Revocation takes effect within seconds.
The competitive intelligence API enforces a daily rate limit to ensure fair usage and platform stability. Limits are applied per API key (per organisation).
| Plan | Daily Limit | Reset |
|---|---|---|
| All plans | 1,000 requests/day | Midnight UTC |
Every API response includes rate limit information in the following headers:
| Header | Description | Example |
|---|---|---|
| X-RateLimit-Limit | Maximum requests allowed per day | 1000 |
| X-RateLimit-Remaining | Requests remaining in the current window | 847 |
| Retry-After | Seconds until the limit resets (only present on 429 responses) | 3600 |
When you exceed the daily limit, the API returns a 429 Too Many Requests status with a Retry-After header indicating how many seconds to wait. Best practices:
X-RateLimit-Remaining proactively and throttle when it drops below a threshold (e.g., 50).Retry-After value, then double on each subsequent retry.{
"error": "rate_limited",
"message": "Daily API limit exceeded (1000 requests/day). Resets at midnight UTC.",
"retry_after": 3600
}
All list endpoints return paginated results. Pagination is controlled by two query parameters and described by a meta object in every response.
| Parameter | Type | Default | Constraints |
|---|---|---|---|
| page | integer | 1 | Minimum 1 |
| limit | integer | 25 | Minimum 1, maximum 100 |
Every paginated response wraps results in a data array and includes a meta object:
{
"data": [ ... ],
"meta": {
"page": 2,
"limit": 25,
"total": 73,
"total_pages": 3
}
}
To retrieve all records, keep incrementing page until page equals total_pages. Example in Python:
import requests
all_changes = []
page = 1
while True:
resp = requests.get(
"https://rivalcheck.com/api/v1/changes",
headers={"Authorization": "Bearer rc_live_YOUR_KEY"},
params={"page": page, "limit": 100}
)
data = resp.json()
all_changes.extend(data["data"])
if page >= data["meta"]["total_pages"]:
break
page += 1
print(f"Fetched {len(all_changes)} total changes")
The API uses standard HTTP status codes. Error responses have a machine-readable error code and a human-readable message:
{
"error": "not_found",
"message": "No page 42."
}
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, invalid or revoked API key |
| 402 | payment_required | Your plan or monitoring subscription has ended |
| 402 | upgrade_required | Your organisation doesn't have API access enabled |
| 404 | not_found | No page, edit or tracked competitor matches the id or name you sent |
| 422 | missing_argument / invalid_url | A required parameter is missing or the URL can't be fact-checked |
| 422 | already_decided | The edit has already been accepted, rejected or marked stale |
| 422 | limit_reached | You already have the maximum number of fact-checked pages or drafts |
| 429 | rate_limited | Daily limit reached; see the Retry-After header |
Facts, pages, corrections, gaps, drafts and the audit log — the data behind RivalCheck’s setup and monitoring. The MCP server exposes the same operations as tools.
/api/v1/facts
Every sourced fact about your company and its rivals. Rival values are quoted from the rival’s own website and only appear once verified; each carries its source_url and checked_at time. Your own values are corroborated when your site confirms them, otherwise self_asserted.
| company | Optional. A rival name, domain or id to narrow the results to your column plus that rival. |
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/facts?company=typeform"
{
"companies": [
{ "id": 3, "name": "Acme Forms", "role": "self", "website_url": "https://acmeforms.com" },
{ "id": 4, "name": "Typeform", "role": "competitor", "website_url": "https://www.typeform.com" }
],
"facts": [
{
"company": "Typeform",
"company_role": "competitor",
"dimension": "Starting price",
"kind": "price",
"value": "$29/mo",
"status": "verified",
"source_url": "https://www.typeform.com/pricing/",
"checked_at": "2026-09-29T06:12:40Z"
}
],
"note": "Rival values are quoted from each rival's own website on the checked_at date. Cite source_url when you use one."
}
/api/v1/pages
Your comparison pages: published pages RivalCheck fact-checks (with a findings summary) and drafts RivalCheck wrote. status is pending, ready or failed (with error set).
| kind | Optional. published or draft. |
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/pages?kind=published"
{
"pages": [
{
"id": 12,
"kind": "published",
"status": "ready",
"url": "https://acmeforms.com/vs/typeform",
"title": "Acme Forms vs Typeform",
"competitor": null,
"audited_at": "2026-09-29T07:00:12Z",
"updated_at": "2026-09-29T07:00:12Z",
"error": null,
"summary": {
"claims": 14, "contradicted": 2, "agrees": 10, "unchecked": 2, "pending_judgement": 0,
"text": "2 of 14 claims contradict the rival's own site."
}
}
]
}
/api/v1/pages/:id
One page in full. Published pages include findings — each with a verdict of contradicted, agrees, minor_difference or unchecked, what your page claims, what the rival’s page says, the source URL and when it was verified. Every page includes its proposed_edits; drafts include markdown with frontmatter.
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/pages/12"
{
"page": {
"id": 12,
"kind": "published",
"status": "ready",
"url": "https://acmeforms.com/vs/typeform",
"title": "Acme Forms vs Typeform",
"summary": { "claims": 14, "contradicted": 2, "agrees": 10, "unchecked": 2, "pending_judgement": 0, "text": "..." },
"findings": [
{
"competitor": "Typeform",
"dimension": "Starting price",
"verdict": "contradicted",
"your_claim": "$50/mo",
"rival_page_says": "$29/mo",
"source_url": "https://www.typeform.com/pricing/",
"source_verified_at": "2026-09-29T06:12:40Z",
"strength": "high",
"reason": "Typeform's pricing page lists Basic at $29/mo billed annually."
}
],
"proposed_edits": [ { "id": 88, "status": "proposed", "...": "see /api/v1/edits" } ],
"markdown": null
}
}
/api/v1/pages
Enrol a page you publish for fact-checking and weekly re-checks. Runs in the background and returns 202 Accepted straight away — poll GET /api/v1/pages/:id until status is ready.
| url | Required. The live URL of your comparison, vs or alternatives page. |
curl -X POST -H "Authorization: Bearer rc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://acmeforms.com/vs/typeform" }' \
https://rivalcheck.com/api/v1/pages
// 202 Accepted
{
"page": { "id": 12, "kind": "published", "status": "pending", "url": "https://acmeforms.com/vs/typeform", "...": "..." },
"message": "Reading the page. Poll get_page (GET /api/v1/pages/12) until status is ready — usually a few minutes."
}
/api/v1/edits
Corrections RivalCheck proposes for your pages: the exact original_text on your page and the suggested_text to replace it with, plus the source that justifies it. Find and replace in your own content, then record the decision.
| status | Optional. proposed (default), accepted, rejected, stale or all. |
| page_id | Optional. Only edits for this page. |
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/edits?page_id=12&status=proposed"
{
"edits": [
{
"id": 88,
"page_id": 12,
"status": "proposed",
"competitor": "Typeform",
"dimension": "Starting price",
"original_text": "Typeform starts at $50/mo.",
"suggested_text": "Typeform starts at $29/mo (Basic, billed annually).",
"your_claim": "$50/mo",
"rival_page_says": "$29/mo",
"source_url": "https://www.typeform.com/pricing/",
"source_captured_at": "2026-09-29T06:12:40Z",
"strength": "high",
"reason": "Typeform's pricing page lists Basic at $29/mo billed annually.",
"decided_at": null
}
]
}
/api/v1/edits/:id
One proposed correction, in the same shape as the list.
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/edits/88"
{
"edit": { "id": 88, "page_id": 12, "status": "proposed", "...": "..." }
}
/api/v1/edits/:id
Record your decision. It’s written to the audit log. Only edits still proposed can be decided; anything else returns 422 already_decided.
| status | Required. accepted or rejected. |
curl -X PATCH -H "Authorization: Bearer rc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "accepted" }' \
https://rivalcheck.com/api/v1/edits/88
{
"edit": { "id": 88, "status": "accepted", "decided_at": "2026-09-30T10:14:02Z", "...": "..." }
}
/api/v1/gaps
Rivals you track but don’t have a comparison page (published or drafted) for yet. sourced_facts is how many verified facts RivalCheck holds for that rival; ready_to_draft says whether that’s enough to draft a page.
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/gaps"
{
"gaps": [
{
"competitor": { "id": 7, "name": "Jotform", "website_url": "https://www.jotform.com" },
"kind": "versus",
"suggested_title": "Acme Forms vs Jotform",
"sourced_facts": 9,
"ready_to_draft": true,
"reason": "You compete with Jotform but publish no page comparing the two."
}
]
}
/api/v1/drafts
Draft “<Your company> vs <Rival>” as Markdown. The comparison table is quoted from sourced facts — never generated — with footnotes to each source; the prose is fair to the rival. Runs in the background and returns 202 Accepted; poll GET /api/v1/pages/:id for the markdown. If a draft for that rival already exists it’s returned unchanged unless you pass replace: true.
| competitor | Required. Rival name, domain or id. |
| brief | Optional. Tone and positioning notes. Never used as a source of facts. |
| replace | Optional boolean. Redraft an existing draft for this rival. |
curl -X POST -H "Authorization: Bearer rc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "competitor": "Typeform", "brief": "Plain, no superlatives", "replace": false }' \
https://rivalcheck.com/api/v1/drafts
// 202 Accepted
{
"page": { "id": 15, "kind": "draft", "status": "pending", "title": "Acme Forms vs Typeform", "...": "..." },
"message": "Drafting. Poll get_page (GET /api/v1/pages/15) until status is ready — usually under a minute."
}
/api/v1/ledger
The hash-chained audit trail, newest first: audits requested, drafts requested, corrections accepted or rejected, and who did it. Each entry’s digest covers the previous entry’s, and chain_intact tells you whether the whole chain verifies. Page back with before_id set to next_before_id.
| limit | Optional. 1–200, default 50. |
| before_id | Optional. Return entries older than this id. |
curl -H "Authorization: Bearer rc_live_YOUR_KEY" \ "https://rivalcheck.com/api/v1/ledger?limit=50"
{
"entries": [
{
"id": 301,
"event": "edit_accepted",
"actor": "api",
"subject": { "type": "proposed_edit", "id": 88 },
"data": { "...": "..." },
"recorded_at": "2026-09-30T10:14:02.123456Z",
"previous_digest": "9f2c…",
"digest": "41ab…"
}
],
"chain_intact": true,
"next_before_id": null
}
Your maintained comparison, tracked competitors, change feeds, battle cards and account usage. Same key, same limits. List endpoints here paginate with page and limit (see Pagination).
/api/v1/comparison
Core resource
Your maintained, evidenced competitor comparison as structured data — every claim carries its verification state, cited source URL, capture date and verification time. Render it in your own site, feed it to your CRM or agents, or diff it between pulls. Competitor claims only reach verified with a source and an archived snapshot; your own claims stay unsubstantiated until your own site corroborates them.
| company | Optional. Narrow to your column vs the named rival(s) — the head-to-head slice. Comma-separated company names (case-insensitive, Rival Inc or rival-inc) or column ids. |
| verified | Backed by a cited public source and an archived capture, within the re-check cadence. |
| changed | The cited live page no longer matches the published value — a proposed change is attached for review. |
| unsubstantiated | A self-asserted claim your own site doesn’t confirm yet. |
| unverifiable | No public statement found — itself a competitive finding. |
{
"comparison": {
"title": "Acme vs Rival Inc and CompeteCo",
"slug": "acme-vs-the-field",
"published": true,
"public_url": "https://rivalcheck.com/comparisons/acme-vs-the-field",
"reverify_cadence": "daily",
"last_reverified_at": "2026-07-02T05:04:11Z",
"next_reverify_at": "2026-07-03T05:04:11Z",
"companies": [
{ "id": 3, "name": "Acme", "role": "self", "website_url": "https://acme.com" },
{ "id": 4, "name": "Rival Inc", "role": "competitor", "website_url": "https://rival.com" }
],
"dimensions": [
{
"id": 2,
"name": "Starting price",
"kind": "price",
"claims": [
{
"company_id": 4,
"company": "Rival Inc",
"value": "$119/mo",
"state": "verified",
"needs_review": false,
"self_asserted": false,
"source_url": "https://rival.com/pricing",
"source_captured_at": "2026-07-02T05:04:09Z",
"verified_at": "2026-07-02T05:04:11Z",
"proposed_change": null
}
]
}
]
},
"meta": { "claims": 24, "verified": 19, "liabilities": 1, "generated_at": "2026-07-02T09:00:00Z" }
}
/api/v1/comparison/liabilities
Claims whose cited live page no longer matches what you publish — each with the standing value, the proposed replacement, and the evidence behind it. Poll this (or subscribe to the comparison.liability webhook event) to route drift into Slack, your CRM, or an agent workflow.
{
"liabilities": [
{
"dimension": "Public API",
"dimension_id": 5,
"company": "Rival Inc",
"company_id": 4,
"company_role": "competitor",
"published_value": "None",
"published_source_url": "https://rival.com/docs",
"last_verified_at": "2026-06-24T19:13:32Z",
"proposed_change": {
"value": "Beta",
"source_url": "https://rival.com/changelog",
"captured_at": "2026-07-02T07:12:00Z"
}
}
],
"meta": { "count": 1, "generated_at": "2026-07-02T09:00:00Z" }
}
/api/v1/competitors
Returns a paginated list of all competitors your organisation is monitoring. This is typically the starting point for any integration with the competitor monitoring API: fetch your competitor list, then drill into changes or battle cards for each.
| Name | Type | Default | Description |
|---|---|---|---|
| status | string | all | Filter by status. One of: pending, discovering, active, paused, error, review_pages |
| page | integer | 1 | Page number (min 1) |
| limit | integer | 25 | Results per page (min 1, max 100) |
{
"data": [
{
"id": "comp_8xKm2Nq",
"name": "Acme Corp",
"website_url": "https://www.acmecorp.com",
"status": "active",
"pages_count": 4,
"changes_count": 17,
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-22T14:33:00Z",
"has_battle_card": true,
"created_at": "2026-02-10T09:00:00Z"
},
{
"id": "comp_3pRtYvW",
"name": "BetaRival",
"website_url": "https://betarival.io",
"status": "active",
"pages_count": 3,
"changes_count": 8,
"last_checked_at": "2026-03-24T06:20:00Z",
"last_change_at": "2026-03-19T10:05:00Z",
"has_battle_card": true,
"created_at": "2026-02-15T11:30:00Z"
},
{
"id": "comp_9dLwZxJ",
"name": "NewStart.io",
"website_url": "https://newstart.io",
"status": "discovering",
"pages_count": 0,
"changes_count": 0,
"last_checked_at": null,
"last_change_at": null,
"has_battle_card": false,
"created_at": "2026-03-24T08:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 25,
"total": 3,
"total_pages": 1
}
}
| pending | Just created. Discovery has not started yet. |
| discovering | RivalCheck is crawling the website to identify key pages (pricing, product, about, etc.). |
| review_pages | Discovery is complete. Pages have been suggested and are awaiting your review before monitoring begins. |
| active | Actively monitored. Pages are checked regularly and changes are detected automatically. |
| paused | Monitoring paused by user. No checks are performed. Historical data is retained. |
| error | An error occurred during discovery or monitoring (e.g., site unreachable, blocked by firewall). |
/api/v1/competitors/:id
Returns the full profile of a single competitor, including all monitored pages, the five most recent changes, and battle card availability. This endpoint is ideal for building competitor detail views in custom dashboards or for feeding context to an AI agent preparing a sales call briefing.
| id | string, required | The competitor's public ID (e.g., comp_8xKm2Nq) |
{
"data": {
"id": "comp_8xKm2Nq",
"name": "Acme Corp",
"website_url": "https://www.acmecorp.com",
"status": "active",
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-22T14:33:00Z",
"has_battle_card": true,
"battle_card_generated_at": "2026-03-20T12:00:00Z",
"created_at": "2026-02-10T09:00:00Z",
"pages": [
{
"id": "page_Kx8mNq2",
"url": "https://www.acmecorp.com/pricing",
"page_type": "pricing",
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-22T14:33:00Z",
"changes_count": 5
},
{
"id": "page_Rt3pYvW",
"url": "https://www.acmecorp.com/product",
"page_type": "product",
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-15T09:20:00Z",
"changes_count": 7
},
{
"id": "page_Lw9dZxJ",
"url": "https://www.acmecorp.com/about",
"page_type": "about",
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-01T16:45:00Z",
"changes_count": 3
},
{
"id": "page_Qn7bMvF",
"url": "https://www.acmecorp.com/enterprise",
"page_type": "landing",
"last_checked_at": "2026-03-24T06:15:00Z",
"last_change_at": "2026-03-18T11:10:00Z",
"changes_count": 2
}
],
"recent_changes": [
{
"id": "chg_Xp4kLm8",
"change_type": "pricing",
"severity": "major",
"summary": "Enterprise tier price increased from $299/mo to $349/mo. New 'Scale' tier added at $199/mo.",
"detected_at": "2026-03-22T14:33:00Z"
},
{
"id": "chg_Yn5jKn9",
"change_type": "content",
"severity": "moderate",
"summary": "Product page updated with new AI features section. Three new feature cards added highlighting 'AI Assistant', 'Smart Reports', and 'Predictive Analytics'.",
"detected_at": "2026-03-15T09:20:00Z"
}
]
}
}
/api/v1/competitors
Start monitoring a new competitor. RivalCheck will automatically crawl the website, identify key pages (pricing, product, about, blog, etc.), and begin monitoring them for changes. The competitor begins in pending status and progresses through discovering before becoming active. This process typically takes 1-3 minutes.
| Field | Type | Required | Description |
|---|---|---|---|
| website_url | string | Yes | Full URL of the competitor's website. Must start with http:// or https://. |
| name | string | No | Display name. If omitted, RivalCheck extracts the company name from the website automatically. |
curl -X POST https://rivalcheck.com/api/v1/competitors \
-H "Authorization: Bearer rc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"website_url": "https://www.newrival.com",
"name": "NewRival"
}'
{
"data": {
"id": "comp_Hk7nRt4",
"name": "NewRival",
"website_url": "https://www.newrival.com",
"status": "pending",
"pages_count": 0,
"changes_count": 0,
"last_checked_at": null,
"last_change_at": null,
"has_battle_card": false,
"created_at": "2026-03-24T10:30:00Z"
},
"message": "Competitor added. Discovery will begin shortly."
}
Tip: After creating a competitor, poll GET /api/v1/competitors/:id to check when the status transitions to active. Alternatively, configure a webhook for the competitor.status_changed event.
/api/v1/competitors/:id/changes
Returns a paginated, filterable list of all changes detected for a specific competitor. Use this when you need a focused view of a single rival's activity, for example when preparing for a deal against that competitor or building a competitor-specific report.
| id | string, required | The competitor's public ID |
| Name | Type | Default | Description |
|---|---|---|---|
| severity | string | all | Filter by severity: minor, moderate, major |
| type | string | all | Filter by change type: pricing, content, layout |
| since | string | none | ISO 8601 datetime. Only return changes detected after this timestamp. Example: 2026-03-01T00:00:00Z |
| page | integer | 1 | Page number |
| limit | integer | 25 | Results per page (max 100) |
{
"data": [
{
"id": "chg_Xp4kLm8",
"competitor_id": "comp_8xKm2Nq",
"competitor_name": "Acme Corp",
"page_url": "https://www.acmecorp.com/pricing",
"page_type": "pricing",
"change_type": "pricing",
"severity": "major",
"summary": "Enterprise tier price increased from $299/mo to $349/mo. New 'Scale' tier added at $199/mo.",
"detected_at": "2026-03-22T14:33:00Z",
"reviewed": false,
"bookmarked": false
},
{
"id": "chg_Yn5jKn9",
"competitor_id": "comp_8xKm2Nq",
"competitor_name": "Acme Corp",
"page_url": "https://www.acmecorp.com/product",
"page_type": "product",
"change_type": "content",
"severity": "moderate",
"summary": "Product page updated with new AI features section. Three new feature cards added.",
"detected_at": "2026-03-15T09:20:00Z",
"reviewed": true,
"bookmarked": false
}
],
"meta": {
"page": 1,
"limit": 25,
"total": 17,
"total_pages": 1
}
}
/api/v1/changes
Returns a unified, paginated feed of changes across all competitors in your organisation. This is the most powerful endpoint in the competitive intelligence API for building dashboards, generating weekly briefings, or feeding a CI aggregation pipeline. Supports filtering by severity, type, competitor, date range, and full-text search.
| Name | Type | Default | Description |
|---|---|---|---|
| severity | string | all | minor, moderate, or major |
| type | string | all | pricing, content, or layout |
| competitor_id | string | none | Filter to a specific competitor's public ID |
| since | string | none | ISO 8601 datetime. Only changes after this timestamp. |
| search | string | none | Full-text search across change summaries and AI analysis. Example: price increase |
| page | integer | 1 | Page number |
| limit | integer | 25 | Results per page (max 100) |
curl -G https://rivalcheck.com/api/v1/changes \ -H "Authorization: Bearer rc_live_YOUR_KEY" \ -d severity=major \ -d type=pricing \ -d since=2026-03-17T00:00:00Z
{
"data": [
{
"id": "chg_Xp4kLm8",
"competitor_id": "comp_8xKm2Nq",
"competitor_name": "Acme Corp",
"page_url": "https://www.acmecorp.com/pricing",
"page_type": "pricing",
"change_type": "pricing",
"severity": "major",
"summary": "Enterprise tier price increased from $299/mo to $349/mo. New 'Scale' tier added at $199/mo.",
"detected_at": "2026-03-22T14:33:00Z",
"reviewed": false,
"bookmarked": false
},
{
"id": "chg_Bm2wQt7",
"competitor_id": "comp_3pRtYvW",
"competitor_name": "BetaRival",
"page_url": "https://betarival.io/pricing",
"page_type": "pricing",
"change_type": "pricing",
"severity": "major",
"summary": "Free tier removed entirely. Lowest plan now starts at $29/mo (previously had a free plan with 100 API calls).",
"detected_at": "2026-03-19T10:05:00Z",
"reviewed": true,
"bookmarked": true
}
],
"meta": {
"page": 1,
"limit": 25,
"total": 2,
"total_pages": 1
}
}
Change types
| pricing | Price adjustments, new tiers, removed plans, trial changes |
| content | Copy changes, new features, messaging shifts, positioning updates |
| layout | Visual redesigns, new sections, structural reorganization |
Severity levels
| major | Significant strategic shifts, large price changes, new product launches |
| moderate | Notable changes worth tracking: new features, messaging updates, minor pricing tweaks |
| minor | Small copy edits, typo fixes, cosmetic adjustments |
/api/v1/changes/:id
Returns the full detail of a specific change, including the AI-generated analysis, before/after snapshots, and A/B testing detection. This is the deepest endpoint in the competitor analysis API, providing everything you need to understand exactly what a competitor changed and what it might mean strategically.
| id | string, required | The change's public ID (e.g., chg_Xp4kLm8) |
{
"data": {
"id": "chg_Xp4kLm8",
"competitor_id": "comp_8xKm2Nq",
"competitor_name": "Acme Corp",
"page_url": "https://www.acmecorp.com/pricing",
"page_type": "pricing",
"change_type": "pricing",
"severity": "major",
"summary": "Enterprise tier price increased from $299/mo to $349/mo. New 'Scale' tier added at $199/mo.",
"detected_at": "2026-03-22T14:33:00Z",
"reviewed": false,
"bookmarked": false,
"ai_analysis": {
"what_changed": "Acme Corp restructured their pricing page with two significant modifications. The Enterprise tier saw a 16.7% price increase from $299/month to $349/month. Simultaneously, they introduced a new 'Scale' tier positioned between their existing Pro ($99/mo) and Enterprise offerings at $199/month.",
"why_it_matters": "This pricing restructure signals that Acme Corp is moving upmarket. The new Scale tier creates a smoother upgrade path, potentially reducing churn at the Pro level while extracting more revenue from mid-market customers. The Enterprise price increase suggests strong demand at that level and confidence in their enterprise value proposition.",
"recommended_action": "Review your own mid-market pricing. If you do not have a comparable tier between $150-$250/month, you may lose deals to Acme's new Scale plan. Consider whether your Enterprise pricing should also be adjusted. Brief the sales team on this change before upcoming deals against Acme.",
"competitive_impact": "high"
},
"ab_test_detected": false,
"snapshots": {
"before": {
"captured_at": "2026-03-20T06:00:00Z",
"content_hash": "a1b2c3d4e5f6..."
},
"after": {
"captured_at": "2026-03-22T14:30:00Z",
"content_hash": "f6e5d4c3b2a1..."
}
}
}
}
About A/B testing detection: When RivalCheck detects that a page is alternating between different versions (e.g., the pricing page shows different prices on different visits), the ab_test_detected field is set to true. This is crucial for the pricing change detection API use case, as it prevents false positives from being treated as permanent changes.
/api/v1/competitors/:id/battle_card
Retrieve the AI-generated battle card for a competitor. Battle cards are a cornerstone of competitive sales enablement, and the battle card API lets you embed them directly into your CRM, sales playbooks, or AI-assisted deal preparation workflows. Cards are generated from the latest monitoring data and include strengths, weaknesses, pricing comparisons, objection handling scripts, and elevator pitches.
| id | string, required | The competitor's public ID |
| Name | Type | Default | Description |
|---|---|---|---|
| format_type | string | full |
full returns all sections in a single structured JSON object.
markdown returns the entire card as a formatted Markdown string (ideal for Slack, Notion, or AI agents).
structured returns sections as an array with section keys and content.
|
{
"data": {
"id": "bc_Vm3xKp7",
"competitor_id": "comp_8xKm2Nq",
"competitor_name": "Acme Corp",
"generated_at": "2026-03-20T12:00:00Z",
"stale": false,
"sections": {
"overview": "Acme Corp is a mid-market B2B SaaS platform focused on project management and team collaboration. Founded in 2019, they have raised $45M in Series B funding and serve approximately 2,000 customers. They position themselves as the 'all-in-one workspace' for growing teams, competing primarily on breadth of features rather than depth in any single area.",
"strengths": [
"Strong brand recognition in the 50-200 employee segment",
"Comprehensive feature set spanning project management, docs, and chat",
"Aggressive content marketing with high-ranking SEO pages",
"Recently added AI features that are getting positive early reviews",
"Free tier available, making bottom-up adoption easy"
],
"weaknesses": [
"Enterprise tier pricing recently increased 16.7%, causing customer complaints on social media",
"No native integrations with industry-specific tools (e.g., Figma, GitHub)",
"Mobile app has 3.2-star rating on App Store with complaints about performance",
"No SOC 2 Type II certification yet (in progress per their security page)",
"Customer support response times averaging 24+ hours based on review sites"
],
"pricing_comparison": {
"their_plans": [
{"name": "Free", "price": "$0/mo", "notes": "Up to 5 users, limited features"},
{"name": "Pro", "price": "$99/mo", "notes": "Up to 25 users, all core features"},
{"name": "Scale", "price": "$199/mo", "notes": "NEW - Up to 100 users, advanced analytics"},
{"name": "Enterprise", "price": "$349/mo", "notes": "Unlimited users, SSO, priority support"}
],
"key_differences": "Acme prices per-workspace while we price per-user. For teams of 10-30, Acme is typically cheaper. Above 30 users, our pricing becomes more competitive. Their new Scale tier directly targets the segment where we have historically won deals.",
"talk_track": "When a prospect mentions Acme's pricing, emphasize our per-user model's predictability and the fact that our Pro plan includes features they charge Enterprise prices for (SSO, API access, custom roles)."
},
"how_we_win": [
"Lead with our superior integration ecosystem (200+ native integrations vs their 45)",
"Demonstrate our mobile experience side-by-side — our 4.7-star app vs their 3.2",
"Emphasize our SOC 2 Type II and GDPR compliance for security-conscious buyers",
"For larger teams (30+), show the TCO comparison where our per-user pricing wins",
"Highlight our 4-hour average support response time with customer testimonials"
],
"objection_handling": [
{
"objection": "Acme has a free tier and you don't.",
"response": "That's true — Acme's free tier is designed to get teams started, but it's limited to 5 users with no integrations or reporting. Our 14-day trial gives you full access to everything, so you can make a real evaluation. Most teams that try both choose us because they see the depth difference immediately."
},
{
"objection": "Acme just launched AI features.",
"response": "We've had AI-powered insights for over a year now. Acme's AI is a first-generation addition. Ask to see a side-by-side comparison of the AI outputs — ours are more actionable because they're trained on deeper data integrations. We're happy to do a proof-of-concept."
},
{
"objection": "Acme seems more established.",
"response": "Acme has strong brand awareness, but look at the trajectory. We've grown 3x year-over-year and have a higher NPS score (72 vs their 54). More importantly, look at which direction each product is heading. Their recent pricing increases and feature additions are playing catch-up in areas where we've led for years."
}
],
"elevator_pitch": "While Acme Corp offers a broad but shallow all-in-one workspace, we provide deeper functionality where it matters most — with 200+ integrations, enterprise-grade security, award-winning mobile experience, and AI that's been battle-tested for over a year. Teams that evaluate both choose us 68% of the time because of the depth difference."
}
}
}
| Section | Description |
|---|---|
| overview | Company background, positioning, market segment, and recent trajectory |
| strengths | Array of the competitor's key advantages and differentiators |
| weaknesses | Array of known weaknesses, gaps, and vulnerabilities |
| pricing_comparison | Detailed pricing breakdown with plans, key differences, and sales talk tracks |
| how_we_win | Array of specific strategies and tactics for winning deals against this competitor |
| objection_handling | Array of common objections with recommended responses |
| elevator_pitch | A concise summary positioning you against this specific competitor |
Staleness: The stale field is true when significant changes have been detected since the battle card was last generated. Use the generate endpoint to refresh it. See our guide on building with the battle cards API for best practices.
/api/v1/competitors/:id/battle_card/generate
Trigger the (re)generation of a battle card for the specified competitor. This is an asynchronous operation: the endpoint returns immediately with a 202 Accepted status. Generation typically takes 15-30 seconds. Poll the GET battle_card endpoint to check for completion, or listen for the battle_card.updated webhook event.
| id | string, required | The competitor's public ID |
curl -X POST https://rivalcheck.com/api/v1/competitors/comp_8xKm2Nq/battle_card/generate \ -H "Authorization: Bearer rc_live_YOUR_KEY"
{
"message": "Battle card generation started.",
"competitor_id": "comp_8xKm2Nq",
"estimated_seconds": 20,
"poll_url": "/api/v1/competitors/comp_8xKm2Nq/battle_card"
}
Polling pattern: After triggering generation, wait 5 seconds, then poll the GET endpoint every 5 seconds until generated_at is more recent than your trigger time. Or, simply use a webhook for a push-based approach.
/api/v1/landscape
Returns a high-level overview of your entire competitive landscape: all competitors with their activity metrics, severity breakdowns, change type distributions, and the latest pricing intelligence. This endpoint powers executive dashboards and weekly competitive briefings. It is one of the most valuable endpoints in the competitive intelligence API for teams that need a bird's-eye view.
{
"data": {
"total_competitors": 5,
"total_changes_30d": 34,
"severity_breakdown": {
"major": 4,
"moderate": 12,
"minor": 18
},
"type_breakdown": {
"pricing": 6,
"content": 19,
"layout": 9
},
"competitors": [
{
"id": "comp_8xKm2Nq",
"name": "Acme Corp",
"status": "active",
"changes_30d": 12,
"last_change_at": "2026-03-22T14:33:00Z",
"severity_breakdown": {"major": 2, "moderate": 5, "minor": 5},
"type_breakdown": {"pricing": 3, "content": 6, "layout": 3},
"latest_pricing_change": {
"id": "chg_Xp4kLm8",
"summary": "Enterprise tier price increased from $299/mo to $349/mo. New 'Scale' tier added at $199/mo.",
"detected_at": "2026-03-22T14:33:00Z",
"severity": "major"
}
},
{
"id": "comp_3pRtYvW",
"name": "BetaRival",
"status": "active",
"changes_30d": 8,
"last_change_at": "2026-03-19T10:05:00Z",
"severity_breakdown": {"major": 1, "moderate": 3, "minor": 4},
"type_breakdown": {"pricing": 1, "content": 5, "layout": 2},
"latest_pricing_change": {
"id": "chg_Bm2wQt7",
"summary": "Free tier removed. Lowest plan now $29/mo.",
"detected_at": "2026-03-19T10:05:00Z",
"severity": "major"
}
},
{
"id": "comp_Zt6mHg3",
"name": "Gamma Solutions",
"status": "active",
"changes_30d": 14,
"last_change_at": "2026-03-23T08:12:00Z",
"severity_breakdown": {"major": 1, "moderate": 4, "minor": 9},
"type_breakdown": {"pricing": 2, "content": 8, "layout": 4},
"latest_pricing_change": null
}
],
"generated_at": "2026-03-24T10:00:00Z"
}
}
/api/v1/account
Returns your current plan details, usage statistics, and feature limits. Useful for building usage dashboards, enforcing client-side limits, or checking whether you can add more competitors before making a POST request.
{
"data": {
"organisation": {
"id": "org_Qm4xNr8",
"name": "My Company Inc"
},
"plan": {
"name": "Growth",
"interval": "monthly",
"price_cents": 4900,
"currency": "usd"
},
"usage": {
"competitors_used": 8,
"competitors_limit": 15,
"pages_used": 32,
"pages_limit": 75,
"api_requests_today": 153,
"api_requests_limit": 1000
},
"features": {
"battle_cards": true,
"webhooks": true,
"mcp_access": true,
"api_access": true,
"email_digests": true,
"custom_pages": true
}
}
}
Complete, copy-paste-ready examples for the most common competitor tracking API operations in multiple languages.
This is the most common use case for the pricing change detection API: retrieve all pricing changes from the past 7 days so your team can respond quickly.
curl
curl -G https://rivalcheck.com/api/v1/changes \ -H "Authorization: Bearer rc_live_YOUR_KEY" \ -d type=pricing \ -d since=2026-03-17T00:00:00Z \ -d limit=50
Python
import requests
from datetime import datetime, timedelta
api_key = "rc_live_YOUR_KEY"
seven_days_ago = (datetime.utcnow() - timedelta(days=7)).isoformat() + "Z"
response = requests.get(
"https://rivalcheck.com/api/v1/changes",
headers={"Authorization": f"Bearer {api_key}"},
params={
"type": "pricing",
"since": seven_days_ago,
"limit": 50,
},
)
response.raise_for_status()
data = response.json()
for change in data["data"]:
print(f"[{change['severity'].upper()}] {change['competitor_name']}")
print(f" {change['summary']}")
print(f" Detected: {change['detected_at']}")
print()
JavaScript (Node.js / fetch)
const API_KEY = process.env.RIVALCHECK_API_KEY;
const sevenDaysAgo = new Date(Date.now() - 7 * 86400000).toISOString();
const params = new URLSearchParams({
type: "pricing",
since: sevenDaysAgo,
limit: "50",
});
const response = await fetch(
`https://rivalcheck.com/api/v1/changes?${params}`,
{
headers: { Authorization: `Bearer ${API_KEY}` },
}
);
if (!response.ok) {
const err = await response.json();
throw new Error(`API error: ${err.error.message}`);
}
const { data, meta } = await response.json();
console.log(`Found ${meta.total} pricing changes in the last 7 days:\n`);
for (const change of data) {
console.log(`[${change.severity.toUpperCase()}] ${change.competitor_name}`);
console.log(` ${change.summary}`);
console.log(` Detected: ${change.detected_at}\n`);
}
Ruby
require "net/http"
require "json"
require "uri"
api_key = ENV.fetch("RIVALCHECK_API_KEY")
since = (Time.now.utc - 7 * 86_400).iso8601
uri = URI("https://rivalcheck.com/api/v1/changes")
uri.query = URI.encode_www_form(type: "pricing", since: since, limit: 50)
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{api_key}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
data["data"].each do |change|
puts "[#{change['severity'].upcase}] #{change['competitor_name']}"
puts " #{change['summary']}"
puts " Detected: #{change['detected_at']}"
puts
end
Retrieve a battle card in markdown format, ready to paste into Slack, Notion, or any other tool your sales team uses. The battle card API supports multiple output formats for exactly this purpose.
curl
curl -G https://rivalcheck.com/api/v1/competitors/comp_8xKm2Nq/battle_card \ -H "Authorization: Bearer rc_live_YOUR_KEY" \ -d format_type=markdown
Python
import requests
api_key = "rc_live_YOUR_KEY"
competitor_id = "comp_8xKm2Nq"
response = requests.get(
f"https://rivalcheck.com/api/v1/competitors/{competitor_id}/battle_card",
headers={"Authorization": f"Bearer {api_key}"},
params={"format_type": "markdown"},
)
response.raise_for_status()
card = response.json()["data"]
# Check if the card is stale and needs regeneration
if card["stale"]:
print("Warning: Battle card is stale. Consider regenerating.")
regen = requests.post(
f"https://rivalcheck.com/api/v1/competitors/{competitor_id}/battle_card/generate",
headers={"Authorization": f"Bearer {api_key}"},
)
print(f"Regeneration triggered: {regen.json()['message']}")
print(card["markdown"])
JavaScript
const API_KEY = process.env.RIVALCHECK_API_KEY;
const competitorId = "comp_8xKm2Nq";
const params = new URLSearchParams({ format_type: "markdown" });
const response = await fetch(
`https://rivalcheck.com/api/v1/competitors/${competitorId}/battle_card?${params}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const { data: card } = await response.json();
if (card.stale) {
console.warn("Battle card is stale. Triggering regeneration...");
await fetch(
`https://rivalcheck.com/api/v1/competitors/${competitorId}/battle_card/generate`,
{ method: "POST", headers: { Authorization: `Bearer ${API_KEY}` } }
);
}
console.log(card.markdown);
Ruby
require "net/http"
require "json"
api_key = ENV.fetch("RIVALCHECK_API_KEY")
competitor_id = "comp_8xKm2Nq"
uri = URI("https://rivalcheck.com/api/v1/competitors/#{competitor_id}/battle_card")
uri.query = URI.encode_www_form(format_type: "markdown")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{api_key}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
card = JSON.parse(response.body)["data"]
puts card["stale"] ? "Warning: card is stale" : "Card is fresh"
puts card["markdown"]
Programmatically add a new competitor and poll until discovery is complete. This pattern is useful for onboarding flows or bulk competitor imports.
Python
import requests
import time
api_key = "rc_live_YOUR_KEY"
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
# Step 1: Add the competitor
create_resp = requests.post(
"https://rivalcheck.com/api/v1/competitors",
headers=headers,
json={"website_url": "https://www.newrival.com", "name": "NewRival"},
)
create_resp.raise_for_status()
competitor = create_resp.json()["data"]
comp_id = competitor["id"]
print(f"Created competitor: {comp_id} (status: {competitor['status']})")
# Step 2: Poll until active
max_attempts = 30
for attempt in range(max_attempts):
time.sleep(10) # Check every 10 seconds
detail_resp = requests.get(
f"https://rivalcheck.com/api/v1/competitors/{comp_id}",
headers=headers,
)
status = detail_resp.json()["data"]["status"]
print(f" Attempt {attempt + 1}: status = {status}")
if status == "active":
pages = detail_resp.json()["data"]["pages"]
print(f"Discovery complete! Monitoring {len(pages)} pages.")
break
elif status == "error":
print("Discovery failed. Check the website URL.")
break
else:
print("Timed out waiting for discovery.")
JavaScript
const API_KEY = process.env.RIVALCHECK_API_KEY;
const headers = {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// Step 1: Add the competitor
const createResp = await fetch("https://rivalcheck.com/api/v1/competitors", {
method: "POST",
headers,
body: JSON.stringify({
website_url: "https://www.newrival.com",
name: "NewRival",
}),
});
const { data: competitor } = await createResp.json();
console.log(`Created: ${competitor.id} (${competitor.status})`);
// Step 2: Poll until active
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
for (let attempt = 0; attempt < 30; attempt++) {
await sleep(10_000);
const resp = await fetch(
`https://rivalcheck.com/api/v1/competitors/${competitor.id}`,
{ headers }
);
const { data } = await resp.json();
console.log(` Attempt ${attempt + 1}: ${data.status}`);
if (data.status === "active") {
console.log(`Discovery complete! Monitoring ${data.pages.length} pages.`);
break;
}
if (data.status === "error") {
console.error("Discovery failed.");
break;
}
}
Practical patterns showing how teams use the competitor monitoring API in production.
A SaaS company runs a cron job every morning that calls GET /api/v1/changes?type=pricing&since=<yesterday>&severity=major. If results are returned, it posts a formatted message to the #competitive-intel Slack channel, tags the VP of Product, and creates a task in Linear. The pricing change detection API ensures no pricing move goes unnoticed.
Endpoints used: GET /changes
Before every sales call, an AI agent (via MCP or API) fetches the battle card for the deal's primary competitor and the last 30 days of changes. It generates a custom briefing document with talking points, objection responses, and a pricing comparison table. The sales rep receives this in their inbox 30 minutes before the call. The battle card API and competitor analysis API work together here.
Endpoints used: GET /competitors/:id/battle_card, GET /competitors/:id/changes
Every Monday, a scheduled script calls GET /api/v1/landscape and GET /api/v1/changes?since=<7_days_ago>, then feeds the results to an LLM with a prompt template to generate an executive-ready competitive briefing as a PDF. The report covers competitor activity trends, notable moves, and strategic implications.
Endpoints used: GET /landscape, GET /changes
A Salesforce integration uses webhooks to receive change.detected events in real time. When a pricing change is detected for a competitor involved in an active deal, the integration automatically adds a note to the opportunity in Salesforce and alerts the deal owner. Battle cards are synced nightly to custom Salesforce objects using the competitor tracking API.
Endpoints used: Webhooks, GET /competitors, GET /competitors/:id/battle_card
A competitive intelligence team joining RivalCheck uses a script to add 15 competitors via POST /api/v1/competitors in sequence, with a 5-second delay between each. They then monitor the GET /api/v1/account endpoint to track usage against their plan limits. After all competitors reach active status, they trigger battle card generation for each one.
Endpoints used: POST /competitors, GET /account, POST /competitors/:id/battle_card/generate
Start with a setup sprint or pick a paid plan, create an API key in Settings, and pull your first facts. The same key works with the MCP server.
Need help? Reach us at support@rivalcheck.com
We use essential cookies to keep the site working. With your consent, we may also use analytics cookies to improve the experience. Privacy policy.