API Reference

HTTP API for Ray9 — base URL, authentication, errors, snippets, retry policy, and a per-endpoint reference generated from the OpenAPI spec.

The Ray9 API is plain JSON over HTTPS — the same SEO data the MCP server uses, exposed as conventional HTTP endpoints. Every endpoint page under this tab is generated from the canonical OpenAPI spec — request bodies, responses, examples, and the interactive playground all stay in lockstep with the live API. This landing page covers the conventions that apply across every endpoint; each endpoint page covers its own contract.

Base URL

https://api.ray9.ai

All authenticated endpoints sit under /v1/…. The unauthenticated /health endpoint is unversioned at the root.

Authentication

Authorization: Bearer rk_…

Send this header on every request except /health. Keys have an rk_ prefix and 32 lowercase alphanumeric characters. Issue and rotate at Settings → API keys; see Authentication for the full lifecycle.

Versioning

The API is currently versioned in the URL path (/v1/…). Breaking changes will ship behind new major versions; non-breaking additions land in /v1 directly.

Error envelope

Every 4xx and 5xx response uses the same shape:

{
  "requestId": "req_…",
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please retry after the indicated delay.",
    "details": { "retryAfterMs": 1200 }
  }
}

requestId mirrors the x-request-id response header so you can correlate bodies to logs without parsing headers separately. Build error handling against error.code, never against error.message (we may rephrase messages; codes are stable).

The full code table lives in Concepts → Errors.

Rate limits

Inbound traffic is limited per organization at 60 requests / 60 seconds, shared across REST, MCP, CLI, and authenticated dashboard calls. On a hit, Ray9 returns 429 rate_limited with Retry-After (seconds) plus details.retryAfterMs (milliseconds). See Concepts → Rate limits.

There are no x-ratelimit-* headers on success responses today — bucket state is intentionally opaque. If you need proactive pacing, the body of any error response gives precise reset timing.

Headers worth knowing

HeaderDirectionWhen
Authorization: Bearer rk_…RequestRequired on every authenticated endpoint.
Content-Type: application/jsonRequestRequired on every POST.
x-request-idResponseOn every error and on /v1/... JSON success responses. The same value is in the body's requestId field, which is universal — prefer the body field.
Retry-AfterResponseOn 429 and 503. Whole seconds.
x-ray9-credit-balanceResponseOn successful billed calls. Post-debit balance in mils (1/1000 USD).

A complete example

curl https://api.ray9.ai/v1/serp/search \
  -H "Authorization: Bearer rk_…" \
  -H "Content-Type: application/json" \
  -d '{ "keyword": "best crm 2026", "depth": 10 }'

Defaults fill in engine: "google", location: "United States", language: "English", device: "desktop". The full parameter list and response shape live on the SERP page.

A successful response includes requestId, the resolved query, creditsCharged, creditsRemaining, totalResultsCount, and a results array of structured SERP items.

Snippet patterns by language

The API is plain JSON over HTTPS, so any HTTP client works. A few minimal idioms:

Python (httpx)

import httpx

response = httpx.post(
    "https://api.ray9.ai/v1/serp/search",
    headers={"Authorization": f"Bearer {api_key}"},
    json={"keyword": "best crm 2026", "depth": 10},
    timeout=60.0,
)
response.raise_for_status()
data = response.json()

TypeScript (fetch)

const response = await fetch("https://api.ray9.ai/v1/serp/search", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ keyword: "best crm 2026", depth: 10 }),
});
const data = await response.json();

Go (net/http)

body, _ := json.Marshal(map[string]any{
    "keyword": "best crm 2026",
    "depth":   10,
})
req, _ := http.NewRequest("POST", "https://api.ray9.ai/v1/serp/search", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)

Set a generous read timeout (60s+) — SERP calls typically take 4–8s but can run longer at high depth.

  • 5xx — exponential backoff with jitter (min(retryAfterMs * 2^n, 30000) with ±50% jitter).
  • 429 — honour Retry-After / details.retryAfterMs; back off with jitter.
  • 4xx other than 429 — don't retry. Fix the request first.

The Ray9 API is read-only, so retries are at-most-once safe by construction.

Pricing

Each successful billed call debits a fixed credit cost. Today only POST /v1/serp/search is billed (5 mils, $0.005). GET /v1/usage and GET /health are free. See Billing for the meter rules and plan tiers.

Endpoints

The sidebar lists every endpoint. Use the interactive playground on each page to send live requests with your own API key — request and response shapes are pulled directly from the OpenAPI spec.

On this page