SelfScale API
Call SelfScale from your own code with a personal API key. Today the supported programmatic interface is the MCP endpoint: JSON-RPC 2.0 over HTTPS.
On this page
Overview
SelfScale exposes one programmatic endpoint, https://selfscale.app/api/mcp. It speaks the Model Context Protocol over Streamable HTTP, which is plain JSON-RPC 2.0 sent with an HTTP POST. AI clients such as Claude and Cursor use it for you, and you can call it directly with curl or any HTTP library.
REST API
Every tool available with a personal API key is read-only. To connect an AI assistant instead of writing code, see the MCP server guide.
Authentication
Create a personal API key in SelfScale under Settings › Developer. Keys start with ss_live_, are shown once, and can be revoked at any time. Send the key on every request in the Authorization header:
Authorization: Bearer ss_live_...A missing, malformed, revoked or expired key gets 401 Unauthorized with a WWW-Authenticate: Bearer challenge. Keep keys out of source control and client-side code; treat a key like a password.
Base URL and request format
| Item | Value |
|---|---|
| Endpoint | POST https://selfscale.app/api/mcp |
| Content-Type | application/json |
| Accept | application/json, text/event-stream (both are required) |
| Body | One JSON-RPC 2.0 message per request. Batches (arrays) are refused. |
| Response | A JSON-RPC 2.0 response with Content-Type application/json. |
| Health check | GET https://selfscale.app/api/mcp returns the server name and version, no key needed. |
The server is stateless, so you can call tools/list and tools/call directly. Sending an initialize request first is optional; it returns the server's capabilities and protocol version:
curl -s https://selfscale.app/api/mcp \
-H "Authorization: Bearer $SELFSCALE_API_KEY" \
-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":"my-script","version":"1.0.0"}}}'List tools
tools/list returns every tool your key can call, with its title, description, JSON Schema for its arguments and annotations such as readOnlyHint.
curl -s https://selfscale.app/api/mcp \
-H "Authorization: Bearer $SELFSCALE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_voice_profile",
"title": "Get my voice profile",
"description": "Your SelfScale voice profile: the rules your writing follows, your signature phrases, words you avoid, and your tone settings. Use it to write in your voice.",
"inputSchema": {
"type": "object",
"properties": {}
},
"annotations": {
"title": "Get my voice profile",
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": false
}
},
{
"...": "8 more"
}
]
}
}Call a tool
tools/call runs one tool. Put the tool name in params.name and its arguments in params.arguments. The tool's result comes back as JSON text inside result.content[0].text.
curl -s https://selfscale.app/api/mcp \
-H "Authorization: Bearer $SELFSCALE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_voice_profile","arguments":{}}}'{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\"success\":true,\"hasProfile\":true,\"profile\":{\"name\":\"My voice\",\"active\":true,\"version\":3,\"updatedAt\":\"2026-10-02T12:00:00.000Z\"},\"voice\":{\"learned\":true,\"rules\":[\"Short sentences.\",\"Open with a concrete number.\"],\"signaturePhrases\":[\"Here's the thing\"],\"avoidedWords\":[\"leverage\",\"synergy\"]},\"tone\":{\"...\":\"your tone settings\"},\"toneOmitted\":false}"
}
]
}
}Every available tool, its arguments and an example response are listed in the tools reference.
Rate limits
Each key can send up to 60 requests a minute. Each account also has a number of calls per day, set by plan and reset at midnight UTC. Every request counts, including tools/list and initialize.
| Plan | Calls per day |
|---|---|
| Standard | Not included |
| Premium | 25 calls per day |
| Power | 25 calls per day |
| Pro (existing subscribers) | 5 calls per day |
Usage headers
Successful responses carry your daily usage:
| Header | Meaning |
|---|---|
X-MCP-Limit | Calls allowed today |
X-MCP-Used | Calls used today, including this one |
X-MCP-Remaining | Calls left today |
Going over either limit returns 429. Wait for the reset (the response body includes resetAt for the daily limit), then retry. Back off and retry on 503 after the number of seconds in Retry-After.
Errors
HTTP errors
The request was refused before any tool ran. The body is JSON with error and message.
| Status | When | What to do |
|---|---|---|
400 | The body was a JSON-RPC batch (an array of messages). | Send one message per request. |
401 | The Authorization header is missing, or the key is wrong, revoked or expired. | Check the header reads Bearer followed by your full key, or create a new key. |
403 | Your plan doesn't include MCP, or the request came from a browser origin that isn't allowed. | MCP comes with Premium and Power. Browser pages can't call it; call from a server, script or desktop app. |
406 | The Accept header doesn't list both application/json and text/event-stream. | Send Accept: application/json, text/event-stream. |
413 | The request body is larger than 256 KB. | Send smaller arguments. |
429 | You went over 60 requests a minute, or your plan's calls for the day. | Wait for Retry-After or the daily reset (resetAt), then retry. |
503 | SelfScale couldn't check your key, plan or usage just then, or MCP is paused. Nothing was run. | Retry after the seconds in Retry-After. |
Tool errors
The request was accepted but the tool could not finish. The HTTP status is 200, the JSON-RPC result has isError: true, and content[0].text holds {"error": CODE, "message": "..."}.
| Code | Meaning |
|---|---|
INVALID_INPUT | An argument is missing, the wrong type, out of range or too long. The message says which. Arguments that don't match the schema come back as "Invalid input" with details. |
UNAUTHORIZED | Your key or plan can't use this tool (the analytics tools need Full Analytics), or a connected app rejected SelfScale's stored connection. The message says which; for a connected app, reconnect it on the Integrations page. |
NOT_FOUND | The thing you asked for doesn't exist, or the feature isn't available on your account yet. |
RATE_LIMITED | A connected app (or that tool's own limit) asked to slow down. Try again later. |
UNAVAILABLE | Your data couldn't be read just then. Nothing changed; try again. |
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\"error\":\"NOT_FOUND\",\"message\":\"Competitors is not available on your account yet.\"}"
}
],
"isError": true
}
}A malformed JSON-RPC message gets a standard JSON-RPC error (for example -32600 invalid request or -32602 invalid params) instead of a result.
Versioning
The endpoint follows the MCP specification. The server negotiates the protocol version in initialize; the version used in these examples is 2025-06-18. Tool names are stable. New tools and new optional arguments can be added at any time, so ignore fields you don't recognize. Breaking changes, such as a renamed tool, are listed on this page.