Concepts
Ideas behind cursor-agent-sdk—how to think before you read the API list. Hands-on steps: Quickstart.
The API vs this library
Anchor: Cursor runs cloud work on their servers; this SDK only sends HTTP requests from your machine.
Cursor exposes a hosted Cursor Cloud Agents API (Cursor Cloud Agents API docs). “Cloud agents” are remote jobs tied to GitHub repos or PRs.
cursor-agent-sdk packages those calls in Python. It does not embed Cursor’s IDE, run models locally, or guarantee PR outcomes—only what the API returns.
What this is NOT
A replacement for the official HTTP docs (behaviour and limits live there).
A way to drive the desktop Cursor app; it is dashboard-style Cloud Agents, via key + HTTP.
Authentication
Anchor: Your Cloud Agents API key is the credential; the client attaches it to every request (Basic auth: key as username, empty password).
Get the key under Dashboard → Cloud Agents. Treat it like a password: env vars or a secret manager, not a committed file.
What this is NOT
The same key family as “any Cursor login” by assumption—use the key type the dashboard labels for Cloud Agents / this API.
Client = one front door to the HTTP API
Anchor: A client is the long-lived object that knows base URL, timeouts, and who you are (the key).
Use one SyncClient or AsyncClient per app or process. Reuse it; do not spin a
new client per call. CursorClient is the same as SyncClient.
What this is NOT
A “session” with a single cloud agent—agents are separate handles (below).
Agent = local bookmark for one remote run
Anchor: An Agent (or AsyncAgent) is a Python object in your process that remembers where work targets GitHub (repo + ref, or pr_url) and, after you start it, which remote run you are talking to (id).
It is not the worker process on Cursor’s side—it is your handle to that run.
agent = client.new_agent(
repo="https://github.com/org/repo",
ref="main",
)
# agent knows "where" — not yet "which run"
After Agent.create(), the handle stores the returned id. Agent.follow_up() sends
more text to that same id. Agent.attach() sets id when you already know it (e.g. after
a restart) and skips a new create on that handle.
What this is NOT
A second create on the same handle for “another task”—that starts a conflicting story. New independent run → new
new_agent(...)(or a new client + agent pattern you choose).
Lifecycle in three beats
Anchor: Pick a repo → start one run → add prompts to that run.
agent = client.new_agent(repo="https://github.com/org/repo", ref="main")
agent.create("Implement feature X.") # first message — creates remote run, stores id
agent.follow_up("Add tests.") # same run
agent.follow_up("Open a PR when ready.") # still same run
Optional extras (model, branch/PR targets, webhooks, images) belong on create, not on routine follow_up—follow-ups are mostly “more text” (and optional images) on the same run.
What this is NOT
create repeated on the same handle for every message—that would fight the model above. First instruction create; later ones follow_up.
Sync vs async
Anchor: Same API shape; sync blocks until each call returns, async uses await and
fits asyncio apps.
Scripts and notebooks →
SyncClient/Agent.Servers or async pipelines →
AsyncClient/AsyncAgent.
What this is NOT
Async is not “stronger”—only choose it when your program is already async.
Helpers vs raw client methods
Anchor: Agent methods keep id and source consistent; client methods like
launch_agent / followup expect you to pass agent_id yourself.
Prefer Agent until you need something only the low-level calls expose.
What this is NOT
Two different backends—same service, different amount of bookkeeping in Python.
When things fail
Anchor: HTTP errors become cursor_agent.CursorAPIError so you can log status_code and
inspect response without guessing strings.