SearchChampSearchChamp
API Documentation

Errors, limits, and usage

Handle SearchChamp API errors, throttling, plan gates, and usage metering safely.

Response envelope

Successes and handler-generated errors share one JSON shape:

{
  "data": null,
  "meta": {},
  "errors": [
    {
      "code": "BAD_REQUEST",
      "message": "A \"site\" query parameter (domain or slug) is required."
    }
  ]
}

errors is empty on success. Do not branch on error-message text; use the HTTP status and errors[].code. Authentication failures rejected at the API Gateway authorizer can be shorter gateway responses rather than this handler envelope.

Status reference

StatusCommon causeWhat to do
200Successful read, including setup_required statesRead data; treat setup_required as an actionable empty state.
400Missing/ambiguous site, unknown route, unrecognized status filter value, malformed PATCH body, or an unsupported target statusCorrect the request. Do not retry unchanged.
401Missing, expired, revoked, or unknown API keyReplace or rotate the key.
403Below-Pro plan, wrong credential type, missing permission, or organization usage ceilingCheck plan, key permissions, and billing/usage.
404Site not found inside the key's organization, or an article/session id that is unknown, malformed, archived, or outside the organizationCall with the exact domain, or verify the id came from the matching feed. The same 404 covers all of these deliberately — a distinguishable response would confirm which ids exist.
405A verb the route does not implement — for example PATCH against a feed path, or POST against anythingRead the Allow header. /v1/api/* implements GET and PATCH only.
409The article or session cannot move to published from its current stateRead meta.current_status and meta.reason. Do not retry unchanged.
429The API key exceeded its per-minute plan allowanceHonor Retry-After when present and retry with backoff.
503Rate limiting or another required guard could not be evaluatedRetry later with bounded exponential backoff. SearchChamp denies the request rather than serving it without protection.

Rate limits

Public /v1/api/* traffic is counted per API key, per minute. One noisy integration therefore does not consume another key's bucket.

PlanRequests per minute per key
Pro300
Agency600
Scale900
Enterprise1,200

The public API is unavailable below Pro even though lower plans have limits for internal application traffic.

On 429, pause new work, honor Retry-After if returned, and retry with jitter. Do not create extra API keys to distribute one integration's traffic; use caching, batching in your own system, and separate keys for operational isolation.

Credits and usage ceilings

Each successful REST call is metered at the same non-zero credit cost as its equivalent MCP read tool:

REST routeEquivalent MCP toolCredits per successful call
GET /v1/api/rankingsget_rankings1
GET /v1/api/ai-visibility/summaryget_ai_visibility1
GET /v1/api/keywordsget_tracked_keywords1
GET /v1/api/audit-issuesget_audit_issues1
GET /v1/api/content/articleslist_content_articles1
GET /v1/api/content/articles/{id}get_article_content1
GET /v1/api/research/sessionslist_research_sessions1
GET /v1/api/research/sessions/{id}get_research_session1
PATCH /v1/api/content/articles/{id}set_content_article_status5
PATCH /v1/api/research/sessions/{id}set_research_session_status5

The two status updates cost more than a read because they are state changes, not because they do more work — they contact no third party and no CMS. A repeat call that reports alreadyPublished: true is a successful call and is metered like any other.

Denied and invalid calls do not run the underlying read or write and are not debited as successful tool work. The organization-wide monthly usage ceiling also applies; once reached, requests receive 403 until usage becomes available again. The ceiling is checked before both verbs — a status update is never treated as a cheaper request than a read.

Retry pattern

async function searchChampGet(path, params, apiKey) {
  const url = new URL(path, 'https://api.searchchamp.com');
  for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (response.ok) return response.json();
    if (![429, 503].includes(response.status)) throw new Error(`SearchChamp API ${response.status}`);

    const retryAfter = Number(response.headers.get('retry-after'));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1000
      : (2 ** attempt) * 500 + Math.random() * 250;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  throw new Error('SearchChamp API retry budget exhausted');
}

Version and compatibility boundary

The route prefix is /v1. SearchChamp has not published a broader deprecation schedule yet. Build only against fields documented in the endpoint reference, tolerate additive fields, and do not infer public support from the web app's internal network calls.

On this page