The error body shape, every error code the RankParse API returns, whether you are charged, and how to retry safely.
Failed requests return a non-2xx HTTP status and a JSON body with a stable machine-readable code. Branch on code, not on the human-readable text, which can change.
Stable identifier. Always present on errors the API raises deliberately.
error
Short label, normally the HTTP status text (Bad Request, Too Many Requests).
message
Human-readable explanation, often with the fix.
retry_after
Seconds to wait. Present on daily quota errors and on retryable 503 errors.
retry
true or false on data store 503 errors, telling you whether an identical retry can succeed.
Variations you should handle:
Live fetch endpoints (page-seo, tech-stack, site-health, schema-markup, sitemap) return error as a short description such as Invalid URL and may omit message.
Approval inbox, agent task and some outreach errors set error to the same value as code.
Unexpected server faults return { "error": "Internal Server Error", "message": "..." } with no code.
Unknown routes return a plain text 404 Not Found, not JSON.
A domain with no data is never an error. It returns 200 with data: []. See Response format.
domain is missing or is not a bare hostname (letters, digits, dots and hyphens, at least one dot, up to 253 characters). https://stripe.com is rejected.
Send a hostname such as stripe.com.
400
invalid_domains
domains, domain_a, domain_b or vs is missing or has too few valid domains (domain-overlap needs at least 2). On batch, domains must be a non-empty array with at least one valid entry.
Fix the parameter.
400
too_many_domains
More domains than allowed (domain-overlap allows 5, batch allows 50).
Split the request.
400
same_domain
competitor-gap was called with domain equal to vs.
Compare two different domains.
400
invalid_sort
sort on backlinks is not importance or recent.
Use a supported value.
400
invalid_from_domain
from_domain on backlinks is not a valid domain.
Fix the value.
400
invalid_link_type
link_type on backlinks has invalid characters.
Use letters, digits, _ or -, up to 32 characters.
400
invalid_url
url is missing or is not a valid public http or https URL (no credentials, no # fragment, no surrounding whitespace). Also returned when domain is invalid on the live endpoints tech-stack, site-health and sitemap.
Send a full public URL, or a bare hostname for domain endpoints.
400
private_ip
The URL or domain points at a private or reserved address.
Use a publicly reachable host.
400
missing_platform
platform-domains was called without platform.
Add platform.
400
unknown_platform
platform is not one of the supported platforms. The message lists them.
Use a listed platform.
400
filter_not_available
registered_after, registered_before or country was sent to platform-domains.
On page-performance: the shared daily PageSpeed quota is used up. retry_after is the seconds until 00:00 UTC. On /v1/tools/*: the free daily limit for your IP or account is used up, with reset_at as a millisecond timestamp.
Wait for the reset.
429
user_quota_exceeded
You made 50 page-performance calls today (UTC), cached calls included.
Try again after 00:00 UTC.
429
tools_quota_exceeded
The free page-speed tool's daily capacity is used up.
Try again tomorrow.
429
google_ads_user_quota_exceeded
You used your 100 Google Ads keyword calls today (UTC).
Wait until 00:00 UTC (retry_after).
429
google_ads_daily_quota_exceeded
The service-wide Google Ads allowance is used up for today.
Google rejected the request (often a bad language or location resource name).
Use names like languageConstants/1000 and geoTargetConstants/2840.
400
invalid_language
language is not a language resource name.
Use languageConstants/<id>.
400
invalid_geo_target
geo_target_constants is not an array of location resource names.
Use geoTargetConstants/<id>.
400
invalid_json
The body is not valid JSON.
Send valid JSON.
400
missing_seed
keyword-ideas needs keywords and/or url.
Provide a seed.
400
missing_keywords
keyword-metrics needs a keywords array.
Provide keywords.
401
google_ads_token_expired
The Google Ads connection expired.
Reconnect in the dashboard.
403
google_ads_developer_token_not_approved
RankParse's Google Ads access level is still pending, so live data is unavailable. Nothing is wrong with your account.
Try again later.
403
google_ads_customer_not_enabled
The selected Ads account is inactive (never set up or deactivated).
Pick another account or activate this one.
403
google_ads_access_denied
Google denied the request, for example a manager (MCC) account was selected. Manager accounts are not supported.
Select a regular Ads account.
404
google_ads_account_not_found
The selected account was not found.
Reselect the account.
429
google_ads_quota_exceeded
Google's own API quota was exceeded.
Retry later.
502
google_ads_token_refresh_failed
The access token could not be refreshed.
Retry shortly.
502
google_ads_api_error
Google Ads returned an unexpected error.
Retry.
503
google_ads_not_configured
The integration is not configured on the server.
Contact support.
The Google Ads daily limits (google_ads_user_quota_exceeded, google_ads_daily_quota_exceeded) are listed under rate and quota limits above. A call counts against them only after Google returns a successful answer.
curl -i "https://api.rankparse.com/v1/domain-authority?domain=github.com" \ -H "X-API-Key: rp_your_key"# Inspect the status line, then the JSON body's "code" field.
import osimport randomimport timeimport requestsAPI = "https://api.rankparse.com/v1"HEADERS = {"X-API-Key": os.environ["RANKPARSE_API_KEY"]}RETRYABLE = {"rate_limited", "query_timeout", "upstream_error", "psi_unavailable", "indexnow_unavailable", "timeout", "fetch_failed"}class RankParseError(Exception): def __init__(self, status, code, message): super().__init__(f"{status} {code}: {message}") self.status, self.code, self.message = status, code, messagedef call(path, params=None, max_attempts=4): for attempt in range(max_attempts): resp = requests.get(f"{API}{path}", params=params, headers=HEADERS, timeout=60) if resp.ok: return resp.json() try: body = resp.json() except ValueError: body = {} code = body.get("code", "unknown") message = body.get("message") or body.get("error", "") retryable = (code in RETRYABLE or resp.status_code in (500, 502, 503)) and body.get("retry") is not False if not retryable or attempt == max_attempts - 1: raise RankParseError(resp.status_code, code, message) # Honor retry_after when present, otherwise exponential backoff with jitter. delay = body.get("retry_after") or min(2 ** attempt, 30) if delay <= 300: # do not sleep until tomorrow on a daily quota time.sleep(delay + random.random()) else: raise RankParseError(resp.status_code, code, message)try: print(call("/domain-authority", {"domain": "github.com"})["data"])except RankParseError as err: if err.code == "insufficient_credits": print("Out of credits: https://rankparse.com/pricing") else: raise