Skip to content
RankX AI
RankX AI Docs
MCP serverConnect a client

Scripts and CI

Call RankX AI's MCP server directly with a bearer token. The JSON-RPC sequence in cURL, Node and Python, and why OAuth is not an option here.

For a script, a cron job or a CI pipeline, use a personal access token in an Authorization header and speak JSON-RPC to POST /api/mcp directly. This is the only supported path for an unattended caller, and it is not a legacy one.

OAuth cannot work here. RankX AI's authorisation server implements the authorisation-code flow with PKCE and nothing else, so every OAuth credential requires a human at a browser. Refresh tokens also rotate with theft detection: a daemon that fails to persist the new value is not degraded, it is disconnected.

Create a token for the job

In RankX AI's MCP settings (owner-only), create a token and give it only the scopes the job needs. A nightly reporting script wants read and nothing more. A tool outside a token's scopes is not listed and not callable, so a read-only token cannot be made to write, spend or publish even by a bug in your script.

Store it wherever you store secrets. Never in the repository.

The sequence

Three calls, in order:

  1. initialize, which is where the connection identifies itself.
  2. tools/list, which returns exactly the tools your token's scopes reach, with the current price already written into the description of every tool that spends.
  3. tools/call, one per tool.
RANKXAI_TOKEN="rxai_your_token_here"
ENDPOINT="https://app.rankxai.com/api/mcp"

# 1. initialize
curl -sS "$ENDPOINT" \
  -H "Authorization: Bearer $RANKXAI_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "capabilities": {},
      "clientInfo": { "name": "reporting-script", "version": "1.0.0" }
    }
  }'

# 2. tools/list
curl -sS "$ENDPOINT" \
  -H "Authorization: Bearer $RANKXAI_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'

# 3. tools/call
curl -sS "$ENDPOINT" \
  -H "Authorization: Bearer $RANKXAI_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": { "name": "list_projects", "arguments": {} }
  }'

What comes back

Every tool returns three things, and which one you want depends on the caller:

  • structuredContent, the machine-readable payload. This is what a script should read.
  • content, a human-readable rendering for text-only clients.
  • _meta, which carries the Website acted on, a deep link into RankX AI, and for a tool that spends, the credits charged and the balance remaining.

Every tool also publishes an output schema in tools/list, so a strict client can validate what it receives.

Rules a script has to respect

Poll the three dispatch-and-poll pairs. start_site_audit, generate_article and get_keyword_trends return an id immediately and do the work in the background. A script that treats the first response as the answer will record an empty result as a failure. Poll with the matching status tool at a sensible interval, and do not re-dispatch a job that is already in flight.

Read the rate limits as real limits. Two windows, both per account and shared across every credential on it: 600 calls an hour, and a tighter 120 an hour for every call whose scope is not read. A write consumes one of each, so a publishing loop hits the second one first. Both fail closed: if the limiter cannot be read, the request is denied. Back off rather than retrying immediately.

Never treat null as zero. Any metric field can be null, and null means unmeasured. A report that renders it as 0% is telling its reader they are invisible when nothing was measured.

Handle a refusal by reading which one it is. There are four, with four different remedies, and only one of them is fixed by adding credits. See credits and metering.

Do not spend on a schedule without a ceiling. The spend tools exist so an agent can act, not so a cron job can run a full sweep nightly by accident. Check the balance first with get_credit_balance, which is free.

Identify your client

clientInfo in initialize is worth setting to something meaningful. RankX AI records it per credential, so a connection that identifies itself as reporting-script is distinguishable from one that does not identify itself at all, which is what you want when you are working out where a spend came from.

Where to go next