API Reference
Quickstart
Working curl, Node, and Python examples for the most common HeyCMO API tasks.
Quickstart
Working examples for the four things AI agents most often want to do with HeyCMO. Every snippet runs end-to-end — copy, paste, replace hcmo_live_... with your key, ship.
0. Get an API key
Sign up at https://heycmo.ai/signin. The first 30 days are free. Your key is shown once at the end of the magic-link flow — store it securely.
For a programmatic flow:
# 1. Create a checkout session
curl -X POST https://heycmo.ai/api/checkout \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com"}' | jq -r .url
# → opens Stripe; complete checkout
# 2. After completion, exchange the session for the key
curl https://heycmo.ai/api/session/SESSION_ID | jq .apiKey1. Run an SEO + GEO scan on any URL
curl
CUSTOMER_ID=... # your customerId (returned with the apiKey)
KEY=hcmo_live_...
curl -X POST https://heycmo.ai/api/scan/$CUSTOMER_ID \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'Response (truncated):
{
"scanId": "scn_...",
"status": "done",
"report": {
"seoScore": 78,
"geoScore": 64,
"checklist": [
{ "title": "Add llms.txt", "fix": "...", "agentId": "ronan" }
],
"llmsTxt": "# example.com\n..."
}
}Node (TypeScript / ESM)
const res = await fetch(`https://heycmo.ai/api/scan/${customerId}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.HEYCMO_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com" }),
});
if (!res.ok) throw new Error(`scan failed: ${res.status}`);
const { report } = await res.json();
console.log(report.seoScore, report.geoScore);Python
import os, requests
r = requests.post(
f"https://heycmo.ai/api/scan/{os.environ['CUSTOMER_ID']}",
headers={"Authorization": f"Bearer {os.environ['HEYCMO_KEY']}"},
json={"url": "https://example.com"},
timeout=60,
)
r.raise_for_status()
report = r.json()["report"]
print(report["seoScore"], report["geoScore"])2. Trigger a research run + watch progress
curl + MCP
The cleanest way to invoke run_research is through the MCP server. From a one-off shell:
KEY=hcmo_live_...
CUST=...
# Open SSE
curl -N "https://heycmo.ai/mcp/sse?token=$KEY" &
SSE=$!
# In another terminal, send a JSON-RPC tool call
curl -X POST "https://heycmo.ai/mcp/message?token=$KEY&sessionId=SID" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"run_research","arguments":{"customerId":"'"$CUST"'"}}}'For real use, drive the MCP via Claude Desktop / Cursor / @heycmo/cli instead of curl.
3. List pending-approval content
CLI
heycmo content listcurl
curl "https://heycmo.ai/api/content/$CUSTOMER_ID?status=pending_approval&limit=50" \
-H "Authorization: Bearer $KEY" | jq '.content[] | {id, type, title}'4. Subscribe to webhooks
curl -X POST "https://heycmo.ai/api/hooks/$CUSTOMER_ID" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-agent.example.com/heycmo-hook",
"events": ["content.published", "workflow.completed"]
}'Verify a webhook payload (Node):
import crypto from "node:crypto";
const sig = req.headers["x-heycmo-signature"];
const expected = crypto.createHmac("sha256", process.env.HEYCMO_HOOK_SECRET)
.update(rawBody)
.digest("hex");
if (sig !== expected) return res.status(401).end();Error handling
const res = await fetch(url, init);
if (res.status === 429) {
const retry = Number(res.headers.get("retry-after") ?? 1);
await new Promise(r => setTimeout(r, retry * 1000));
// retry…
}
if (!res.ok) {
const { error, code, requestId } = await res.json();
throw new Error(`${code ?? res.status}: ${error} (request ${requestId})`);
}See For AI agents for full retry policy + discovery file index.