API Reference

The Stepcheck base URL is https://api-stepcheck.plxi.us. Every endpoint accepts and returns JSON and is authenticated with your API key.

curl -X POST https://api-stepcheck.plxi.us/v1/example/analyze \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
  "text": "The quick brown fox jumps over the lazy dog. The dog sleeps.",
  "top": 3
}'
New accounts include 100 free requests per month, no card required. Create an account and grab your key from the dashboard.

Authentication

Send an X-API-Key header with every request. Keys are created and revoked in the dashboard; a key is shown once, when it is created, so store it somewhere safe.

X-API-Key: sk_live_...
Keep keys on your server. Anyone with your key can spend your quota, so never put it in browser JavaScript or a public repository.

Plans & limits

Limits apply per account, not per key: every key on the account (and teammates working in it) shares them. There is a per-minute rate, a cap on requests in flight at once, and a monthly quota. On the Free plan the quota resets on the 1st of each month (UTC); on paid plans it resets on your billing-period anniversary. Requests that fail validation are never charged.

PlanPriceRequests / minIn flightRequests / month
FreeFree101100
Starter$10/mo6032,000
Pro$50/mo300620,000

Errors

Errors return JSON with a stable error code, an optional message, and, on rate-limit and monthly-quota 429s, reset_at. A request that returns an error is never charged.

{
  "error": "rate_limit_exceeded",
  "reset_at": "2026-07-22T18:04:00Z"
}

Fix the request

StatusCodeWhenWhat to do
400
bad_request
The body is not valid JSON.Send the body as JSON.
400
invalid_input
A body field failed the operation’s checks; message says which one and why.Correct that field.
401
invalid_api_key
The X-API-Key header is missing, malformed, revoked or not a key we issued.Check the key, or create a new one in the dashboard.
403
account_blocked
The account is suspended.Contact [email protected].
404
not_found
No endpoint at that path for that method: operations take POST, /v1/usage takes GET.Check the path and the method.
413
payload_too_large
The body is larger than the endpoint takes: 1 MiB unless it says otherwise.Send a smaller body.

Wait, then retry

StatusCodeWhenWhat to do
429
rate_limit_exceeded
The per-minute request limit is reached (per account, across all its keys). Requests that then fail, for example on invalid input, count toward it too.Retry after reset_at, the start of the next minute.
429
concurrency_limit_exceeded
The account has as many requests in flight as its plan allows.Retry with backoff once one of them finishes. There is no reset_at.
429
monthly_quota_exceeded
The quota for this billing period is used up.Retry after reset_at: the 1st of next month (UTC) on Free, the end of your billing period on paid plans. Or upgrade.

Temporary: retry with backoff

StatusCodeWhenWhat to do
400
body_read_failed
The connection failed while the body was being sent.Retry.
408
request_body_timeout
The body took too long to arrive.Retry, sending the whole body at once.
500
internal_error
Something failed on our side.Retry with backoff.
503
capacity_unavailable
The API is handling as many requests as it can.Retry with backoff.
503
database_unavailable
limits_unavailable
Our database, or the store that counts rate limits, is unavailable.Retry with backoff.
503
auth_busy
API key checks are busy.Retry with backoff.
503
billing_period_stale
billing_period_invalid
On a paid plan, the current billing period can’t be confirmed, for example because a renewal hasn’t been recorded yet.Retry later. If it lasts, the account owner can open Billing in the dashboard to refresh it, or contact [email protected].
504
request_timeout
The operation ran past the API’s time limit.Retry with backoff; if it keeps happening, send smaller input.
A response without a JSON body comes from the proxies in front of the API, for example a 413 for a body far over the limit or a 502 while the API restarts. Retry the 5xx ones with backoff.

Use with AI agents (MCP)

Stepcheck is also an MCP server, so Claude and other AI agents can call every endpoint below as a tool. Tool calls are charged exactly like API calls, to the same plan and quota, and failed calls are free.

https://api-stepcheck.plxi.us/mcp

Claude (claude.ai and the desktop app): Settings → Connectors → Add custom connector, then paste the URL above. Claude asks you to sign in with your Plexsicity account; calls are then charged to your own account.

Claude Code, Cursor, VS Code and other clients: send an API key from the dashboard as a header. Calls are charged to the account that owns the key, so a team’s key spends the team’s quota. Clients that only take a bearer token can send Authorization: Bearer sk_live_… instead.

claude mcp add --transport http stepcheck https://api-stepcheck.plxi.us/mcp \
  --header "X-API-Key: sk_live_..."
Tools: analyze_text, slugify_text. Tools take URLs, not file uploads. Results are JSON, also returned as structured content.

POST/v1/example/analyze

Word, sentence and character counts, reading time and the most used words. Costs 1 request.

FieldTypeDescription
textstringRequired. The text to analyze.
top?integerHow many of the most used words to list. Default 5.
Example request body
{
  "text": "The quick brown fox jumps over the lazy dog. The dog sleeps.",
  "top": 3
}
Example response · 200
{
  "characters": 60,
  "characters_no_spaces": 49,
  "credits": 1,
  "paragraphs": 1,
  "reading_time_seconds": 4,
  "sentences": 2,
  "top_words": [
    {
      "count": 2,
      "word": "dog"
    },
    {
      "count": 1,
      "word": "brown"
    },
    {
      "count": 1,
      "word": "fox"
    }
  ],
  "words": 12
}
curl -X POST https://api-stepcheck.plxi.us/v1/example/analyze \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
  "text": "The quick brown fox jumps over the lazy dog. The dog sleeps.",
  "top": 3
}'

The response is the operation’s result plus credits, what the request was charged. Try it in the Playground. MCP tool: analyze_text.

POST/v1/example/slugify

Turn text into a URL-safe slug (accents removed, lowercase). Costs 1 request.

FieldTypeDescription
max_length?integerThe longest slug to return, in characters. Default 80.
separator?"-" | "_" | "."The character between words. Default "-".
textstringRequired. The title or phrase to turn into a slug.
Example request body
{
  "max_length": 80,
  "separator": "-",
  "text": "Héllo Wörld: a new post!"
}
Example response · 200
{
  "credits": 1,
  "slug": "hello-world-a-new-post"
}
curl -X POST https://api-stepcheck.plxi.us/v1/example/slugify \
  -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
  "max_length": 80,
  "separator": "-",
  "text": "Héllo Wörld: a new post!"
}'

The response is the operation’s result plus credits, what the request was charged. Try it in the Playground. MCP tool: slugify_text.

GET/v1/usage

Your plan and usage this billing period, for alerting before you reach the quota.

curl https://api-stepcheck.plxi.us/v1/usage \
  -H "X-API-Key: sk_live_..."
Response 200
{
  "plan": "starter",
  "used": 487,
  "limit": 2000,
  "period_start": "2026-07-18T09:12:44Z",
  "period_end": "2026-08-18T09:12:44Z"
}

Webhooks

Add an HTTPS endpoint under Webhooks in the dashboard and Stepcheck POSTs a request.completed event to it after each charged request. Each endpoint gets its own signing secret (whsec_…), shown once when you add it.

POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Stepcheck-Webhooks/1
X-Stepcheck-Event: request.completed
X-Stepcheck-Event-ID: 6f1c2a7e-…:0d4e9b2c-…
X-Stepcheck-Signature: sha256=3b5f0c9e41d7…

{"data": {"ms": 4, "credits": 1, "endpoint": "example.analyze"}, "event": "request.completed", "created_at": "2026-09-29T14:03:11.482107+00:00"}
Verify the signature

X-Stepcheck-Signature is sha256=<hex>: the HMAC-SHA256 of the raw body, keyed with the endpoint’s full secret including whsec_. Compute it over the bytes you received, before parsing:

import crypto from 'node:crypto';

// rawBody: the request body exactly as received (Buffer or string). Verify it
// before parsing -- JSON that has been parsed and re-serialised won't match.
// secret: the endpoint's full signing secret, "whsec_" prefix included.
function verifySignature(rawBody, signatureHeader, secret) {
  const expected =
    'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const given = Buffer.from(String(signatureHeader || ''));
  const want = Buffer.from(expected);
  return given.length === want.length && crypto.timingSafeEqual(given, want);
}

// Express: take this route's body raw, verify, then parse.
app.post('/your/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const ok = verifySignature(req.body, req.get('X-Stepcheck-Signature'), process.env.WEBHOOK_SECRET);
  if (!ok) return res.sendStatus(401);
  const { event, data } = JSON.parse(req.body);
  res.sendStatus(200);
});
Reply with any 2xx within 8 seconds. Anything else, including a redirect, a timeout or a connection error, is retried after 2, 4, 8, 16, 32, 64 and 128 seconds: 8 attempts in all. Every attempt carries the same X-Stepcheck-Event-ID, so use it to ignore duplicates. Recent attempts are listed under Deliveries on the Webhooks page. Endpoints must be public HTTPS URLs.