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
| Status | Common cause | What to do |
|---|---|---|
200 | Successful read, including setup_required states | Read data; treat setup_required as an actionable empty state. |
400 | Missing/ambiguous site, unknown route, invalid request | Correct the request. Do not retry unchanged. |
401 | Missing, expired, revoked, or unknown API key | Replace or rotate the key. |
403 | Below-Pro plan, wrong credential type, missing permission, or organization usage ceiling | Check plan, key permissions, and billing/usage. |
404 | Site not found inside the key's organization | Call with the exact domain or verify the site belongs to the organization. |
429 | The API key exceeded its per-minute plan allowance | Honor Retry-After when present and retry with backoff. |
503 | Rate limiting or another required guard could not be evaluated | Retry 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.
| Plan | Requests per minute per key |
|---|---|
| Pro | 300 |
| Agency | 600 |
| Scale | 900 |
| Enterprise | 1,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 route | Equivalent MCP tool | Credits per successful call |
|---|---|---|
/v1/api/rankings | get_rankings | 1 |
/v1/api/ai-visibility/summary | get_ai_visibility | 1 |
/v1/api/keywords | get_tracked_keywords | 1 |
/v1/api/audit-issues | get_audit_issues | 1 |
Denied and invalid calls do not run the underlying read 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.
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.