CHEATSHEET

API Reference

Programmatic access to your cheats and Tasks, for external tools acting on your behalf (e.g. a local Claude Code project). Every account can generate a key from the User page's API Access section. Free accounts get read-only access; Pro unlocks write access (create/update/delete) plus a much higher daily limit. See Billing for pricing.

Authentication

Send your key as a bearer token on every request:

Authorization: Bearer csk_live_...

Keys are shown to you only once, at creation, and can be revoked at any time from the User page. Each key is scoped to either read or read + write — a write-scoped key can also do everything a read-scoped key can.

Base URL

https://cheats.aarontrotter.com

MCP Connector

For an MCP client (Claude Code, Claude Desktop) rather than a custom integration, add this app directly as a remote MCP server instead of calling the REST endpoints below yourself:

URL: https://cheats.aarontrotter.com/mcp
Header: Authorization: Bearer csk_live_...

Exposes 11 tools over the same key/quota/tier rules as the REST API: search_cheats, get_cheat, get_revisions, search_tasks, get_task (all read-scoped), and add_cheat, update_cheat, delete_cheat, add_task, update_task, delete_task (all write-scoped). Each maps directly to one of the endpoints documented below.

Limits

  • Daily quota: shared across all of an account's keys, resets at midnight UTC, exceeding it returns 429. Free: 50 requests/day, read-only. Pro: 500 requests/day fair use, full read + write.
  • Write burst limit: the write endpoints are additionally capped at 60 requests per 5 minutes.
  • Write endpoints (add/update/delete, for both cheats and Tasks) require the calling account to be Pro; a Free account's key returns 403 on those, but can still use every read endpoint.

Errors

Errors are returned as { "error": "message" } with a matching HTTP status: 400 bad request, 401 missing/invalid key, 403 forbidden (wrong scope, not Pro, not the owner), 404 not found, 429 rate limited, 500 server error.

Endpoints

GET /mcp/search

Search your visible cheats — public cheats plus your own (and any linked account's) private ones.

Scope: read

ParameterTypeRequiredDescription
qstringNoSearch text; omitted means the most recently created cheats
typestringNoRestrict results to one type/category name
limitnumberNoResults per page — default 8, max 25
pagenumberNo0-indexed page number, default 0 — use with hasMore to fetch further pages

Returns { results: [{ id, title, type, snippet, private, stars, points }], total, page, hasMore }. snippet is the cheat body truncated to 400 characters. total is the number of cheats matching the search across all pages; hasMore is true when a further page exists — request it with page + 1.

GET /mcp/search?q=docker&limit=5&page=1
Authorization: Bearer csk_live_...

GET /mcp/getCheat

Fetch the full body of one cheat.

Scope: read

ParameterInTypeRequiredDescription
idquery stringstringYesThe cheat id, as returned by /mcp/search

Returns { id, title, type, body: { text, highlights, links }, private, stars, points }.

404 if the cheat doesn't exist; 403 if it's private and you're not its owner (or a linked account).

GET /mcp/getCheat?id=abc123
Authorization: Bearer csk_live_...

GET /mcp/getRevisions

List the full append-only revision history for a cheat's lineage — every past and current version, oldest first.

Scope: read

ParameterInTypeRequiredDescription
idquery stringstringYesId of any revision in the lineage — not just the original

Returns { originalId, revisions: [{ id, title, type, snippet, private, ceased, createdDateMs, stars, points }] }. Exactly one entry has ceased: false — that's the current version; the others are prior edits kept for history.

404 if the cheat doesn't exist; 403 if it's private and you're not its owner (or a linked account).

GET /mcp/getRevisions?id=abc123
Authorization: Bearer csk_live_...

POST /mcp/addCheat

Create a new cheat. Starts with 0 stars and 0 points regardless of what's sent.

Scope: write

FieldTypeRequiredDescription
titlestringYesMax 40 characters
typeNamestringYesMax 15 characters, case-insensitive; created automatically if it doesn't already exist
body.textstringYesMax 100 lines, 600 characters per line; whole record capped at 10 KB
privatebooleanYesMust be sent explicitly — there is no default

Returns { id } — the new cheat's id.

POST /mcp/addCheat
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "title": "Restart nginx",
  "typeName": "linux",
  "body": { "text": "sudo systemctl restart nginx" },
  "private": true
}

POST /mcp/updateCheat

Revise a cheat you own. This is append-only, same as editing a cheat in the browser: the old revision is marked ceased and a new one is created and linked to it, nothing is overwritten in place.

Scope: write

FieldTypeRequiredDescription
idstringYesId of the cheat to revise
titlestringYesMax 40 characters
typeNamestringYesMax 15 characters, case-insensitive; created automatically if it doesn't already exist
body.textstringYesMax 100 lines, 600 characters per line; whole record capped at 10 KB
privatebooleanYesMust be sent explicitly — there is no default

Returns { id } — the new revision's id (different from the id you sent in). 404 if the original doesn't exist; 403 if you don't own it.

POST /mcp/updateCheat
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "id": "abc123",
  "title": "Restart nginx",
  "typeName": "linux",
  "body": { "text": "sudo systemctl restart nginx --now" },
  "private": true
}

POST /mcp/deleteCheat

Delete a cheat you own. Not a hard delete — same as the browser's delete button, the cheat is marked ceased and stops appearing in search/getCheat, but the record and its revision history aren't erased.

Scope: write

FieldTypeRequiredDescription
idstringYesId of the cheat to delete

Returns { success: true }. 404 if the cheat doesn't exist; 403 if you don't own it.

POST /mcp/deleteCheat
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "id": "abc123"
}

Tasks

Tasks are unencrypted, private-only notes (to-do lists, AI plans, and so on) that live outside cheat search entirely, in a separate collection with its own endpoints below. Unlike cheats they have no type taxonomy (just a freeform category string you choose) and no private field (always private), and no revision history is ever exposed, even though an edit is append-only under the hood. A task is either permanent or auto-expires and is then automatically deleted (soft-ceased).

GET /mcp/searchTasks

Search your own tasks. Not Algolia-backed like cheat search, just a plain title/text/category substring match over your tasks, since a personal task list is small.

Scope: read

ParameterTypeRequiredDescription
qstringNoSearch text matched against title/text/category; omitted means the most recently created tasks
pagenumberNo1-indexed page number, default 1

Returns { tasks: [{ id, title, category, text, highlights, links, createdDateMs, expiresAtMs }], totalPages }. expiresAtMs is null for a permanent task; category is "" if none was set.

GET /mcp/searchTasks?q=groceries
Authorization: Bearer csk_live_...

GET /mcp/getTask

Fetch one task.

Scope: read

ParameterInTypeRequiredDescription
idquery stringstringYesThe task id, as returned by /mcp/searchTasks

Returns { id, title, category, text, highlights, links, createdDateMs, expiresAtMs }. 404 if the task doesn't exist; 403 if you don't own it.

GET /mcp/getTask?id=abc123
Authorization: Bearer csk_live_...

POST /mcp/addTask

Create a new task.

Scope: write

FieldTypeRequiredDescription
titlestringYesMax 40 characters
categorystringNoFreeform grouping label you choose, e.g. "AI" or a project name; max 30 characters. Not a shared taxonomy like cheats' types
textstringYesMax 100 lines, 600 characters per line; whole record capped at 10 KB
durationstringNoOne of permanent, 1h, 1d, 1w; defaults to permanent

Returns { id }, the new task's id.

POST /mcp/addTask
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "title": "Ship the release",
  "category": "AI",
  "text": "1. Tag the branch\n2. Deploy\n3. Announce",
  "duration": "1d"
}

POST /mcp/updateTask

Revise a task you own. Append-only, same as /mcp/updateCheat (the old revision is ceased and a new one created), but there is no endpoint to view that history for Tasks.

Scope: write

FieldTypeRequiredDescription
idstringYesId of the task to revise
titlestringYesMax 40 characters
categorystringNoNew freeform grouping label; max 30 characters
textstringYesMax 100 lines, 600 characters per line; whole record capped at 10 KB
durationstringNoOne of permanent, 1h, 1d, 1w; defaults to permanent

Returns { id }, the new revision's id. 404 if the original doesn't exist; 403 if you don't own it.

POST /mcp/updateTask
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "id": "abc123",
  "title": "Ship the release",
  "category": "AI",
  "text": "1. Tag the branch\n2. Deploy\n3. Announce\n4. Watch dashboards",
  "duration": "1d"
}

POST /mcp/deleteTask

Delete a task you own. Not a hard delete, same soft-cease as /mcp/deleteCheat.

Scope: write

FieldTypeRequiredDescription
idstringYesId of the task to delete

Returns { success: true }. 404 if the task doesn't exist; 403 if you don't own it.

POST /mcp/deleteTask
Authorization: Bearer csk_live_...
Content-Type: application/json

{
  "id": "abc123"
}

Example: .mcp.json

The recommended way to add this as a remote MCP server in Claude Code (or any other client that reads a .mcp.json config) is a small local proxy that reads your key from a gitignored .env.local file and forwards every call to the hosted connector above, so the key never has to sit in .mcp.json itself or in a shell environment variable:

{
  "mcpServers": {
    "cheatsheet": {
      "command": "node",
      "args": ["live-proxy.js"],
      "env": {
        "CHEATSHEET_ENV_FILE": ".env.local"
      }
    }
  }
}

See Setting up the MCP proxy for the full script and step-by-step setup.

If you'd rather skip the local process and are fine putting the key in a real shell environment variable instead, you can also add this as a direct "type": "http" entry:

{
  "mcpServers": {
    "cheatsheet": {
      "type": "http",
      "url": "https://cheats.aarontrotter.com/mcp",
      "headers": {
        "Authorization": "Bearer ${CHEATSHEET_API_KEY}"
      }
    }
  }
}

For a "type": "http" entry, Claude Code can only expand ${...} from a real environment variable already set before it starts, it has no way to read one out of a file for this entry type. Either way, create the key first from the User page's API Access section, choosing read-only or read + write depending on what you want the client to be able to do, and restart Claude Code (or start a new session) after adding the config to approve the cheatsheet server when prompted.