# Get account info
Source: https://docs.carboncopy.inc/api-reference/account/get-account-info
/api-reference/openapi.json get /api/v1/account
Returns information about the authenticated user's Carbon Copy account.
# Batch update followed traders
Source: https://docs.carboncopy.inc/api-reference/follow-management/batch-update-followed-traders
/api-reference/openapi.json patch /api/v1/portfolio/traders/batch
Update copy settings for up to 100 followed traders in a single request.
# Follow a trader
Source: https://docs.carboncopy.inc/api-reference/follow-management/follow-a-trader
/api-reference/openapi.json post /api/v1/portfolio/traders
Start copy-trading a new wallet. Returns `409 Conflict` if already following.
# Get followed trader
Source: https://docs.carboncopy.inc/api-reference/follow-management/get-followed-trader
/api-reference/openapi.json get /api/v1/portfolio/traders/{wallet}
Returns detailed info for a single followed trader including recent trades.
# List followed traders
Source: https://docs.carboncopy.inc/api-reference/follow-management/list-followed-traders
/api-reference/openapi.json get /api/v1/portfolio/traders
Returns all traders you currently follow, including copy settings and performance stats.
# Pause copy trading
Source: https://docs.carboncopy.inc/api-reference/follow-management/pause-copy-trading
/api-reference/openapi.json post /api/v1/portfolio/traders/{wallet}/pause
Temporarily pause copy trading for a specific trader without unfollowing.
# Resume copy trading
Source: https://docs.carboncopy.inc/api-reference/follow-management/resume-copy-trading
/api-reference/openapi.json post /api/v1/portfolio/traders/{wallet}/resume
Resume copy trading for a previously paused trader.
# Unfollow a trader
Source: https://docs.carboncopy.inc/api-reference/follow-management/unfollow-a-trader
/api-reference/openapi.json delete /api/v1/portfolio/traders/{wallet}
Stop copy-trading this wallet. Existing open positions are **not** automatically closed.
# Update follow settings
Source: https://docs.carboncopy.inc/api-reference/follow-management/update-follow-settings
/api-reference/openapi.json patch /api/v1/portfolio/traders/{wallet}
Update copy-trading settings. All fields are optional — only provided fields are changed.
# Health check
Source: https://docs.carboncopy.inc/api-reference/health/health-check
/api-reference/openapi.json get /api/v1/health
Returns API health status. No authentication required.
# API Reference
Source: https://docs.carboncopy.inc/api-reference/introduction
Interactive documentation for all Carboncopy API endpoints. Try requests directly in your browser.
## Overview
The Carboncopy API follows REST conventions. All endpoints:
* Accept and return **JSON**
* Use **Bearer token** authentication (API key or Privy JWT)
* Return consistent [error envelopes](/errors)
* Include [rate limit headers](/rate-limits) on every response
**Base URL:** `https://carboncopy.inc`\
**Version:** `v1` (prefix: `/api/v1`)
***
## Endpoint groups
| Group | Description |
| ----------------------------------------------------- | ------------------------------------------------ |
| [Health](/api-reference/openapi/getHealth) | Unauthenticated health check |
| [Portfolio](/api-reference/openapi/getPortfolio) | Portfolio summary, trade history, open positions |
| [Traders](/api-reference/openapi/listTraders) | Follow, configure, and manage copied traders |
| [Orders](/api-reference/openapi/listOrders) | Inspect orders placed by your copy-trading bot |
| [Account](/api-reference/openapi/getAccount) | Your Carboncopy account info |
| [Key Management](/api-reference/openapi/createApiKey) | Create and revoke API keys (Privy JWT required) |
***
## Authentication in the explorer
To try authenticated endpoints in the interactive explorer:
1. Click **Authorize** at the top of any endpoint
2. Enter your API key as: `cc_your_key_here` (no `Bearer ` prefix — the explorer adds it)
3. For Key Management endpoints, enter your Privy JWT instead
Don't have a key? See the [Quickstart](/quickstart) to create one.
***
## OpenAPI spec
The full OpenAPI 3.1 spec is available at:
```
https://carboncopy.inc/api-reference/openapi.json
```
You can import this into [Postman](https://postman.com), [Insomnia](https://insomnia.rest), or any OpenAPI-compatible tool.
# Get market price
Source: https://docs.carboncopy.inc/api-reference/markets/get-market-price
/api-reference/openapi.json get /api/v1/markets/price
Fetch the current or historical price for a Polymarket token.
# Get order by ID
Source: https://docs.carboncopy.inc/api-reference/orders/get-order-by-id
/api-reference/openapi.json get /api/v1/orders/{id}
Returns a single order by its document ID.
# List orders
Source: https://docs.carboncopy.inc/api-reference/orders/list-orders
/api-reference/openapi.json get /api/v1/orders
Returns a paginated list of orders placed by your copy-trading bot.
# Close a position
Source: https://docs.carboncopy.inc/api-reference/portfolio/close-a-position
/api-reference/openapi.json post /api/v1/portfolio/positions/close
Sell all shares in an open position at the current market price.
# Get P&L time series
Source: https://docs.carboncopy.inc/api-reference/portfolio/get-p&l-time-series
/api-reference/openapi.json get /api/v1/portfolio/pnl-history
Returns time-series portfolio P&L snapshots (captured every 15 minutes) for charting performance over time. Includes realized and unrealized P&L breakdown.
# Get portfolio summary
Source: https://docs.carboncopy.inc/api-reference/portfolio/get-portfolio-summary
/api-reference/openapi.json get /api/v1/portfolio
Returns an aggregated summary of your copy-trading portfolio including live wallet balance, open positions value, unrealized P&L, and lifetime stats.
# List copy trade history
Source: https://docs.carboncopy.inc/api-reference/portfolio/list-copy-trade-history
/api-reference/openapi.json get /api/v1/portfolio/history
Returns a paginated list of all copy trades. Every item includes a `pnl` field (`0` when not yet realized). Also available at `/api/v1/portfolio/trades`.
# List copy trade history (alias)
Source: https://docs.carboncopy.inc/api-reference/portfolio/list-copy-trade-history-alias
/api-reference/openapi.json get /api/v1/portfolio/trades
Alias for `/api/v1/portfolio/history`. Returns identical data.
# List positions
Source: https://docs.carboncopy.inc/api-reference/portfolio/list-positions
/api-reference/openapi.json get /api/v1/portfolio/positions
Returns live open positions (mark-to-market). `status=closed` currently returns an empty set.
# P&L breakdown by market
Source: https://docs.carboncopy.inc/api-reference/portfolio/p&l-breakdown-by-market
/api-reference/openapi.json get /api/v1/portfolio/pnl-by-market
Returns realized P&L and volume grouped by each market you have traded.
# P&L breakdown by trader
Source: https://docs.carboncopy.inc/api-reference/portfolio/p&l-breakdown-by-trader
/api-reference/openapi.json get /api/v1/portfolio/pnl-by-trader
Returns realized P&L and volume grouped by each copied trader.
# Discover traders
Source: https://docs.carboncopy.inc/api-reference/trader-discovery/discover-traders
/api-reference/openapi.json get /api/v1/traders
Search and filter traders on the platform. Use this to find traders worth following based on performance metrics.
# Get trader performance
Source: https://docs.carboncopy.inc/api-reference/trader-discovery/get-trader-performance
/api-reference/openapi.json get /api/v1/traders/{wallet}/performance
Returns detailed performance analytics for any trader on the platform, including P&L history, drawdown, and Sharpe ratio.
# Authentication
Source: https://docs.carboncopy.inc/authentication
Authenticate with the Carboncopy API using scoped API keys.
## Overview
All API endpoints are authenticated using **API keys**. Pass your key as a Bearer token in the `Authorization` header.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://carboncopy.inc/api/v1/portfolio \
-H "Authorization: Bearer cc_your_key_here"
```
***
## API Keys
### Format
API keys follow the format `cc_` followed by 64 lowercase hexadecimal characters:
```
cc_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```
### Creating a key
1. Log in to [Carboncopy](https://www.carboncopy.inc)
2. Navigate to [**Settings → API Keys**](https://www.carboncopy.inc/settings)
3. Click **Create Key**, choose a name and scopes
4. Copy the key — it's shown **exactly once** and cannot be retrieved later
### Scopes
Every key is issued with a set of scopes that control what it can access. Always issue keys with the minimum scopes your use-case requires.
| Scope | Values | Controls |
| ----------- | ----------------------- | ---------------------------------------------------------- |
| `portfolio` | `none`, `read` | View portfolio summary, history, and positions |
| `traders` | `none`, `read`, `write` | List/view traders (`read`); follow/edit/unfollow (`write`) |
| `orders` | `none`, `read`, `write` | View orders (`read`); future order mutations (`write`) |
| `markets` | `none`, `read` | Market data access (coming soon) |
| `account` | `none`, `read` | View account information |
`write` scope implies `read` for the same resource. You don't need to grant both.
### Key lifecycle
* Maximum **10 active keys** per account.
* Keys can have an optional expiry timestamp.
* Revoke a key anytime from [**Settings → API Keys**](https://www.carboncopy.inc/settings).
### Security best practices
Never commit API keys to source control. Use environment variables or a secrets manager.
* **Rotate regularly** — set an expiry and automate rotation in production.
* **Narrow scopes** — monitoring bots only need `portfolio:read`; they don't need `traders:write`.
* **One key per service** — easier to audit, easier to revoke if compromised.
* **Revoke immediately** if a key is exposed.
***
## Error responses
If authentication fails, the API returns a `401 Unauthorized`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing API key."
}
}
```
If you authenticate successfully but your key lacks the required scope, you'll receive `403 Forbidden`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "forbidden",
"message": "This key does not have the required scope."
}
}
```
See [Errors](/errors) for the full error reference.
# Errors
Source: https://docs.carboncopy.inc/errors
Carboncopy uses consistent, machine-readable error codes across all endpoints.
## Error format
All error responses share the same envelope:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "error_code",
"message": "Human-readable description of what went wrong."
}
}
```
The `code` field is a stable string identifier — use this in your error handling logic. The `message` field is for humans and may change over time.
***
## Error codes
| HTTP Status | Code | Description |
| ----------- | -------------------- | --------------------------------------------------------------------- |
| `400` | `bad_request` | Malformed request body or invalid query parameters |
| `401` | `unauthorized` | Missing, invalid, or expired authentication credentials |
| `403` | `forbidden` | Authenticated but key lacks required scope |
| `404` | `not_found` | Resource does not exist (or you don't have access to it) |
| `405` | `method_not_allowed` | HTTP method not supported for this endpoint |
| `409` | `conflict` | Request conflicts with existing state (e.g. already following trader) |
| `422` | `quota_exceeded` | Business rule quota exceeded (e.g. max 10 API keys) |
| `429` | `rate_limited` | Too many requests — back off and retry |
| `500` | `internal_error` | Unexpected server-side error |
***
## Examples
### 400 — Bad request
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "bad_request",
"message": "copyPercentage must be between 0 and 100."
}
}
```
Returned when required fields are missing, types are wrong, or values fall outside allowed ranges.
***
### 401 — Unauthorized
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing API key."
}
}
```
Check that:
* You're sending the `Authorization: Bearer ` header
* The key hasn't been revoked or expired
* For key management endpoints, you're using a Privy JWT, not an API key
***
### 403 — Forbidden
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "forbidden",
"message": "This key does not have the required scope."
}
}
```
Your key is valid but lacks the scope for this operation. For example, calling `POST /api/v1/traders` with a key that only has `traders:read`.
***
### 404 — Not found
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "not_found",
"message": "Trader not found."
}
}
```
The requested resource doesn't exist, or you're not following the specified trader.
***
### 409 — Conflict
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "conflict",
"message": "You are already following this trader."
}
}
```
Returned by `POST /api/v1/traders` when you attempt to follow a wallet you're already copying.
***
### 422 — Quota exceeded
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "quota_exceeded",
"message": "Maximum of 10 API keys per user. Please revoke an existing key first."
}
}
```
Returned when a business rule cap is hit, distinct from a validation error.
***
### 429 — Rate limited
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Retry after 30 seconds."
}
}
```
See [Rate Limits](/rate-limits) for handling strategies.
***
### 500 — Internal error
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "internal_error",
"message": "An unexpected error occurred. Please try again."
}
}
```
This shouldn't happen. If you're seeing consistent 500s, contact [support@carboncopy.inc](mailto:support@carboncopy.inc).
***
## Error handling example
```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
async function callApi(url: string, key: string) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${key}` },
});
if (!response.ok) {
const { error } = await response.json();
switch (error.code) {
case "unauthorized":
throw new Error("Check your API key");
case "forbidden":
throw new Error(`Missing scope for ${url}`);
case "rate_limited":
const retryAfter = response.headers.get("Retry-After");
throw new Error(`Rate limited — retry in ${retryAfter}s`);
case "not_found":
return null; // treat as empty, not fatal
default:
throw new Error(`API error: ${error.message}`);
}
}
return response.json();
}
```
# Carboncopy API
Source: https://docs.carboncopy.inc/index
Programmatic access to Polymarket copy-trading. Follow top traders, automate positions, and monitor your portfolio.
[Carboncopy](https://www.carboncopy.inc) is a copy-trading platform built on [Polymarket](https://polymarket.com). The API gives you direct control over your portfolio — follow traders, manage positions, and audit every order your bot places.
Get your API key and make your first call in under 5 minutes. Full endpoint reference included.
Use Carboncopy from AI agents like Claude, Cursor, or any MCP-compatible client. One-line install.
# MCP Server
Source: https://docs.carboncopy.inc/mcp-server
Use Carboncopy from AI agents via the Model Context Protocol.
## Overview
The Carboncopy MCP server lets AI agents — like Claude, Cursor, or any MCP-compatible client — manage your Polymarket copy-trading portfolio through natural tool calls.
Install it with a single line. No code required.
***
## Quick Install
```bash Claude Code theme={"theme":{"light":"github-light","dark":"github-dark"}}
claude mcp add carboncopy -s user -e CARBONCOPY_API_KEY=cc_your_key -- npx -y @carbon-copy/mcp
```
```bash Codex CLI theme={"theme":{"light":"github-light","dark":"github-dark"}}
codex mcp add carboncopy --env CARBONCOPY_API_KEY=cc_your_key -- npx -y @carbon-copy/mcp
```
```bash OpenCode (interactive) theme={"theme":{"light":"github-light","dark":"github-dark"}}
opencode mcp add
# Select "local", name "carboncopy", command: npx -y @carbon-copy/mcp
```
Replace `cc_your_key` with your actual API key from [Settings](https://www.carboncopy.inc/settings).
***
## Manual Setup
### Claude Desktop
Add to your config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"carboncopy": {
"command": "npx",
"args": ["-y", "@carbon-copy/mcp"],
"env": {
"CARBONCOPY_API_KEY": "cc_your_key_here"
}
}
}
}
```
### Cursor
Add to `.cursor/mcp.json` in your project:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"carboncopy": {
"command": "npx",
"args": ["-y", "@carbon-copy/mcp"],
"env": {
"CARBONCOPY_API_KEY": "cc_your_key_here"
}
}
}
}
```
### Any MCP client
The server uses **stdio transport** — any MCP-compatible client can run it:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
CARBONCOPY_API_KEY=cc_... npx @carbon-copy/mcp
```
***
## Available Tools
### Portfolio
| Tool | Description |
| ----------------------- | ----------------------------------------------- |
| `get_portfolio` | Portfolio summary — total value, P\&L, win rate |
| `get_portfolio_history` | Paginated trade history with date filtering |
| `get_positions` | Currently open positions |
### Trader Discovery
| Tool | Description |
| ------------------------ | ----------------------------------------------------------------- |
| `discover_traders` | Search/filter traders by profit, ROI, win rate, volume, followers |
| `get_trader_performance` | Detailed performance analytics and P\&L history for any trader |
### Follow Management
| Tool | Description |
| ---------------------- | -------------------------------------------------- |
| `list_traders` | All followed traders with copy settings and stats |
| `follow_trader` | Start following a new trader |
| `get_trader` | Details for a single followed trader |
| `update_trader` | Change copy percentage, max amount, notifications |
| `batch_update_traders` | Update up to 100 followed traders in one call |
| `unfollow_trader` | Stop following a trader |
| `pause_trader` | Temporarily pause copy-trading without unfollowing |
| `resume_trader` | Resume paused copy-trading |
### Orders & Account
| Tool | Description |
| ------------- | ----------------------------------------- |
| `list_orders` | Paginated orders with status/date filters |
| `get_order` | Single order by ID |
| `get_account` | Account information |
| `health` | API health check |
***
## Resources
The MCP server also exposes two read-only resources that AI agents can pull for context without a tool call:
| URI | Description |
| ------------------------ | -------------------------- |
| `carboncopy://portfolio` | Current portfolio snapshot |
| `carboncopy://traders` | List of followed traders |
***
## Authentication
Set the `CARBONCOPY_API_KEY` environment variable. The server passes it as a Bearer token on every API call.
Generate a key from [**Settings → API Keys**](https://www.carboncopy.inc/settings). For full agent access, grant `portfolio:read`, `traders:write`, `orders:read`, and `account:read`.
***
## Source Code
The MCP server is open source: [github.com/CarbonCopyInc/carboncopy-mcp](https://github.com/CarbonCopyInc/carboncopy-mcp)
# Pagination
Source: https://docs.carboncopy.inc/pagination
Carboncopy uses cursor-based pagination for all list endpoints. No page numbers — just opaque cursors that stay stable as data changes.
## Overview
List endpoints return results wrapped in a standard `data` / `meta` envelope. Each paginated response includes:
| Field | Type | Description |
| ----------------- | ---------------- | ------------------------------------------------------------------------------- |
| `data.items` | `array` | The records for this page |
| `data.nextCursor` | `string \| null` | Pass this as `cursor` in the next request. `null` when there are no more pages. |
| `data.hasMore` | `boolean` | `true` when more pages exist beyond this one |
| `meta.requestId` | `string` | Unique request identifier for debugging |
| `meta.timestamp` | `integer` | Server timestamp (Unix ms) |
***
## Query parameters
| Parameter | Type | Default | Description |
| --------- | --------- | ------- | ----------------------------------------------------------- |
| `limit` | `integer` | `50` | Records per page (1–200) |
| `cursor` | `string` | — | Cursor from previous response |
| `since` | `integer` | — | Unix timestamp (ms) — include records at or after this time |
| `until` | `integer` | — | Unix timestamp (ms) — include records before this time |
***
## Basic example
**First page:**
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://carboncopy.inc/api/v1/portfolio/history?limit=50" \
-H "Authorization: Bearer $CC_API_KEY"
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": {
"items": [ ... ],
"nextCursor": "eyJpZCI6Imsx...",
"hasMore": true
},
"meta": {
"requestId": "cf7301af-0c1e-4354-85a8-9153db69ae6d",
"timestamp": 1741600000000
}
}
```
**Next page** — pass `nextCursor` as `cursor`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://carboncopy.inc/api/v1/portfolio/history?limit=50&cursor=eyJpZCI6Imsx..." \
-H "Authorization: Bearer $CC_API_KEY"
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": {
"items": [ ... ],
"nextCursor": null,
"hasMore": false
},
"meta": {
"requestId": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"timestamp": 1741600030000
}
}
```
When `hasMore` is `false` (or `nextCursor` is `null`), you've consumed all pages.
***
## Iterating all pages
### Python
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import requests
def fetch_all(endpoint, api_key, **params):
headers = {"Authorization": f"Bearer {api_key}"}
cursor = None
results = []
while True:
query = {**params, "limit": 200}
if cursor:
query["cursor"] = cursor
response = requests.get(endpoint, headers=headers, params=query)
response.raise_for_status()
body = response.json()
results.extend(body["data"]["items"])
if not body["data"].get("hasMore") or not body["data"].get("nextCursor"):
break
cursor = body["data"]["nextCursor"]
return results
# Example
trades = fetch_all(
"https://carboncopy.inc/api/v1/portfolio/history",
api_key="cc_your_key_here",
since=1741000000000,
)
print(f"Fetched {len(trades)} trades")
```
### TypeScript
```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
interface PaginatedResponse {
data: {
items: T[];
nextCursor: string | null;
hasMore: boolean;
};
meta: {
requestId: string;
timestamp: number;
};
}
async function fetchAll(
endpoint: string,
apiKey: string,
params: Record = {}
): Promise {
const results: T[] = [];
let cursor: string | null = null;
do {
const url = new URL(endpoint);
url.searchParams.set("limit", "200");
for (const [k, v] of Object.entries(params)) {
url.searchParams.set(k, String(v));
}
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url.toString(), {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body: PaginatedResponse = await res.json();
results.push(...body.data.items);
cursor = body.data.nextCursor;
} while (cursor !== null);
return results;
}
// Example
const trades = await fetchAll(
"https://carboncopy.inc/api/v1/portfolio/history",
"cc_your_key_here",
{ since: 1741000000000 }
);
```
***
## Time-range filtering
Use `since` and `until` to fetch a specific window without iterating your entire history:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# All trades in the last 7 days
NOW=$(date +%s%3N)
SEVEN_DAYS_AGO=$((NOW - 7 * 24 * 60 * 60 * 1000))
curl "https://carboncopy.inc/api/v1/portfolio/history?since=${SEVEN_DAYS_AGO}&limit=100" \
-H "Authorization: Bearer $CC_API_KEY"
```
For production polling, store the `nextCursor` from your last run and resume from there rather than re-fetching everything.
***
## Notes on cursors
* Cursors are **opaque** — don't parse or modify them.
* Cursors are **stable** — new records arriving during pagination won't cause you to skip or re-see existing records.
* Cursors may expire after a period of inactivity. If you receive a `400 bad_request` error on a cursor, start a fresh request from the beginning.
# Polymarket MCP Capabilities Reference
Source: https://docs.carboncopy.inc/polymarket-mcp/capabilities-reference
Canonical reference for public discovery, batched market data, auth wrappers, bridge, transaction building, relayer flows, prompts, and resources.
## Canonical tool surface
This page reflects the current canonical MCP surface asserted by the server smoke test on `main`.
The server is organized around a smaller set of wrapper-style entry points:
* `list_markets` selects listing shape and retrieval mode with `view` and `mode`
* market data is exposed through pluralized batch tools like `get_order_books` and `get_prices`
* auth flows are centered on `ensure_api_key`, `prepare_order`, `place_order`, `cancel_orders`, `get_rewards`, and `get_rewards_market_details`
* transaction building is unified under `build_transactions`
* relayer execution is unified under `relayer_execute`
## 1) Public market discovery
These tools work without credentials.
| Task | Tools |
| ------------------- | ------------------------------------------------------------------------------------------------------------- |
| Health check | `health_check` |
| Market discovery | `list_markets`, `get_market` |
| Event discovery | `list_events`, `get_event`, `search` |
| Batched market data | `get_order_books`, `get_prices`, `get_midpoints`, `get_spreads`, `get_last_trade_prices`, `get_price_history` |
| Trade-event lookup | `get_market_trade_events` |
| Market metadata | `get_fee_rate_bps`, `get_tick_size`, `get_neg_risk`, `calculate_market_price` |
### `list_markets`
`list_markets` is the canonical listing entry point. It accepts:
* `view` to choose payload shape
* `mode` to choose listing mode
* `next_cursor` for pagination
Example:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"tool": "list_markets",
"input": {
"view": "simplified",
"mode": "sampling"
}
}
```
## 2) Batched market data
Use these tools when you already have one or more `token_id` values and want consistent batch responses.
| Tool | Purpose |
| ----------------------- | --------------------------------------------------- |
| `get_order_books` | Level-2 order book snapshots for `items[].token_id` |
| `get_prices` | Best quotes for `items[].token_id` + `items[].side` |
| `get_midpoints` | Neutral midpoint values by token |
| `get_spreads` | Bid/ask spread values by token |
| `get_last_trade_prices` | Latest trade prices by token |
| `get_price_history` | Historical pricing series for market/token inputs |
Example:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"tool": "get_prices",
"input": {
"items": [
{ "token_id": "", "side": "BUY" },
{ "token_id": "", "side": "SELL" }
]
}
}
```
`items[].side` is required for side-aware tools like `get_prices`. Including `side` in other batched calls is still useful for a uniform workflow shape.
## 3) Authenticated trading and account wrappers
These tools require configured Polymarket credentials.
| Task | Tools |
| ----------------------- | ---------------------------------------------------------------------------------------------------------- |
| L2 key bootstrap | `ensure_api_key`, `get_api_keys`, `delete_api_key` |
| Read-only key lifecycle | `create_readonly_api_key`, `get_readonly_api_keys`, `validate_readonly_api_key`, `delete_readonly_api_key` |
| Account inspection | `get_order`, `get_open_orders`, `get_user_trades`, `get_balance_allowance` |
| Allowance mutation | `update_balance_allowance` |
| Order preparation | `prepare_order` |
| Order posting | `place_order` |
| Order cancellation | `cancel_orders` |
| Rewards | `get_rewards`, `get_rewards_market_details` |
### `prepare_order`
`prepare_order` prepares or signs an order locally without posting it.
* `kind: "limit"` expects `price` and `size`
* `kind: "market"` expects `amount`
### `place_order`
`place_order` is the canonical order-posting wrapper. It uses `source` to select the posting path:
* `source: "draft"` for create-and-post from a draft order
* `source: "signed"` for posting one pre-signed order
* `source: "signed_batch"` for posting a batch of pre-signed orders
`confirm: true` is required.
### `cancel_orders`
`cancel_orders` is the canonical cancellation wrapper. It uses:
* `mode: "ids"`
* `mode: "all"`
* `mode: "market"`
`confirm: true` is required.
### `get_rewards`
`get_rewards` uses a `scope` selector:
* `day`
* `total_day`
* `current`
* `percentages`
* `user_market_config`
Use `get_rewards_market_details` when you need market-specific reward details for a `condition_id`.
## 4) Bridge and transaction building
### Bridge tools
* `bridge_supported_assets`
* `bridge_create_deposit_addresses`
* `bridge_get_transaction_status`
### `build_transactions`
`build_transactions` is the canonical transaction-builder wrapper. Pass an `items` array where each item declares a `kind` plus its required fields.
Representative `kind` values include:
* `erc20_approval`, `erc1155_approval`, `erc20_transfer`, `eth_transfer`
* `withdraw_on_matic`
* `split_positions`, `merge_positions`, `redeem_positions`
* `buy_market_outcome`, `sell_market_outcome`
* `add_funding`, `remove_funding`
* `neg_risk_convert_positions`, `neg_risk_redeem_positions`
* `proxy_wallet_address`, `index_set`, `market_index`
Example:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"tool": "build_transactions",
"input": {
"items": [
{
"kind": "erc20_transfer",
"token_address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"recipient": "0x000000000000000000000000000000000000dEaD",
"amount": "1000000"
}
]
}
}
```
## 5) Relayer and builder-relayer
Use these when transaction lifecycle tracking matters.
### Relayer tools
* `relayer_get_relay_address`
* `relayer_get_nonce`
* `relayer_get_relay_payload`
* `relayer_get_transactions`
* `relayer_execute`
* `relayer_deploy_safe`
* `relayer_poll_until_state`
`relayer_execute` is the canonical relayer execution wrapper. It accepts:
* `execution_type: "proxy" | "safe" | "manual"`
* `transactions`
* optional `metadata`
* optional `gas_limit` / `gas_price` for manual mode
* `confirm: true`
### Builder-relayer tools
* `builder_relayer_get_nonce`
* `builder_relayer_get_relay_payload`
* `builder_relayer_get_transactions`
* `builder_relayer_execute`
* `builder_relayer_deploy`
* `builder_relayer_get_deployed`
* `builder_relayer_poll_until_state`
## 6) Prompts and resources
### Prompts
| Prompt | Purpose |
| --------------------- | -------------------------------------- |
| `market-analysis` | Structured market-analysis workflow |
| `portfolio-review` | Portfolio and risk review workflow |
| `execution-checklist` | Pre-trade execution readiness workflow |
### Resources
| URI | Purpose |
| ------------------------ | ------------------------------------------------- |
| `polymarket://contracts` | Canonical Polygon contract registry |
| `polymarket://status` | Runtime/auth readiness with redacted secret state |
| `polymarket://endpoints` | Canonical REST endpoint registry |
### Resource template
| URI template | Purpose |
| ------------------------------------ | ----------------------------------------------------- |
| `polymarket://markets/{conditionId}` | Market payload for a specific Polymarket condition ID |
# Polymarket MCP Overview
Source: https://docs.carboncopy.inc/polymarket-mcp/overview
Production MCP server for Polymarket market discovery, trading, relayer, bridge, and agent workflows.
## What it is
`@carbon-copy/polymarket-mcp` is a production MCP stdio server for Polymarket.
It exposes a canonical public tool surface for:
* market and event discovery
* batched price, midpoint, spread, and order book retrieval
* authenticated account and trading workflows
* relayer and builder-relayer execution flows
* bridge operations and transaction building
* prompt-driven analysis and runtime resources for agents
The docs in this section track the canonical MCP surface asserted by the server smoke test on `main`.
## Who it is for
Add a stable Polymarket MCP server to Claude, Cursor, or any stdio-compatible MCP client.
Research markets, prepare orders, and execute with explicit confirmation gates for mutations.
Pull repeatable market snapshots, historical price series, event metadata, and rewards context.
Build higher-level workflows on top of canonical wrappers like `list_markets`, `place_order`, `build_transactions`, and `relayer_execute`.
## Major capabilities
* **Public discovery:** `list_markets`, `get_market`, `list_events`, `get_event`, `search`.
* **Batched market data:** `get_order_books`, `get_prices`, `get_midpoints`, `get_spreads`, `get_last_trade_prices`, `get_price_history`.
* **Trading and account:** `ensure_api_key`, `prepare_order`, `place_order`, `cancel_orders`, balances/allowances, and rewards wrappers.
* **Bridge and transaction building:** supported assets, bridge status/deposit helpers, and unified `build_transactions`.
* **Relayer flows:** relayer and builder-relayer nonce, payload, execution, deployment, deployed-status, and polling tools.
* **Agent context surfaces:** prompts `market-analysis`, `portfolio-review`, `execution-checklist`; resources `polymarket://contracts`, `polymarket://status`, `polymarket://endpoints`; resource template `polymarket://markets/{conditionId}`.
## Safety model and secret handling
Keep signer keys and API credentials in environment variables or a secrets manager. Never paste secrets into prompts or commit them to source control.
The server is designed for agent-safe execution:
* **Mutation guard:** state-changing tools require `confirm: true`, including `place_order`, `cancel_orders`, `relayer_execute`, `relayer_deploy_safe`, `builder_relayer_execute`, and `builder_relayer_deploy`.
* **Split read/write surface:** public discovery works without secrets; authenticated tools stay feature-gated behind the required env vars.
* **Redacted runtime status:** `polymarket://status` reports auth/runtime readiness without exposing actual secret values.
* **Wrapper-first design:** canonical wrappers replace older fragmented tools so agents can reason over fewer, more explicit entry points.
## What to read next
One-command MCP client setup with `npx -y @carbon-copy/polymarket-mcp`.
Canonical tool groups, wrapper semantics, prompts, and resources.
A practical agent workflow built on the current pluralized market-data tools.
# Polymarket MCP Quickstart
Source: https://docs.carboncopy.inc/polymarket-mcp/quickstart
Set up @carbon-copy/polymarket-mcp in an MCP client, connect over stdio, and configure runtime/auth environment variables.
## Recommended path: one-command MCP client setup
Use the published package `@carbon-copy/polymarket-mcp` as the default install path.
### Claude CLI
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
claude mcp add polymarket -- npx -y @carbon-copy/polymarket-mcp
```
### Claude Desktop config
Add a local stdio server entry to your Claude Desktop MCP config:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"polymarket": {
"command": "npx",
"args": ["-y", "@carbon-copy/polymarket-mcp"],
"env": {
"POLYMARKET_CHAIN_ID": "137"
}
}
}
}
```
### Installed binary variant
If you prefer a globally installed executable:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm install -g @carbon-copy/polymarket-mcp
polymarket-mcp
```
Example config:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"polymarket": {
"command": "polymarket-mcp",
"env": {
"POLYMARKET_CHAIN_ID": "137"
}
}
}
}
```
## How the connection works
The package runs as a local MCP server over **stdio**.
Your MCP client launches one of these commands:
* `npx -y @carbon-copy/polymarket-mcp`
* `polymarket-mcp`
No HTTP server is required for normal client integration.
## Environment variables
`@carbon-copy/polymarket-mcp` can start with no secrets for public market discovery. Add credentials only for account, trading, rewards, relayer, or builder-relayer workflows.
### Required for basic startup
* None.
### Common runtime variables (optional)
| Variable | Default | Notes |
| ---------------------------- | ----------------------------------- | ------------------------------------------------------------ |
| `POLYMARKET_CHAIN_ID` | `137` | Supports `137` (Polygon) and `80002` (Amoy). |
| `POLYMARKET_SIGNATURE_TYPE` | `0` (`EOA`) | Accepts `EOA`, `POLY_PROXY`, `POLY_GNOSIS_SAFE`, or `0/1/2`. |
| `POLYMARKET_RPC_URL` | none | Optional RPC override. |
| `POLYMARKET_CLOB_URL` | `https://clob.polymarket.com` | CLOB REST base URL. |
| `POLYMARKET_GAMMA_URL` | `https://gamma-api.polymarket.com` | Gamma REST base URL. |
| `POLYMARKET_BRIDGE_URL` | `https://bridge.polymarket.com` | Bridge REST base URL. |
| `POLYMARKET_RELAYER_URL` | `https://relayer-v2.polymarket.com` | Relayer base URL. |
| `POLYMARKET_RELAYER_TX_TYPE` | none | Optional relayer tx type override. |
### Auth variables (optional, feature-gated)
| Capability area | Variables |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| L1 signing readiness | `POLYMARKET_PRIVATE_KEY` (alias: `PRIVATE_KEY`) |
| L2 API auth readiness | `POLYMARKET_API_KEY`, `POLYMARKET_API_SECRET`, `POLYMARKET_API_PASSPHRASE` |
| Builder-relayer readiness | `POLYMARKET_BUILDER_API_KEY`, `POLYMARKET_BUILDER_API_SECRET`, `POLYMARKET_BUILDER_API_PASSPHRASE` |
| Optional extras | `POLYMARKET_ADDRESS`, `POLYMARKET_FUNDER` (aliases: `FUNDER_ADDRESS`, `POLYMARKET_FUNDER_ADDRESS`), `POLYMARKET_BUILDER_AUTH_HEADER`, `POLYMARKET_GEOBLOCK_TOKEN`, `POLYMARKET_USER_AGENT` |
Public discovery tools work without auth. Auth-backed wrappers such as `ensure_api_key`, `get_open_orders`, `place_order`, `get_rewards`, and relayer execution flows require the relevant credentials.
## Minimal smoke check
After connecting in your MCP client, run these in order:
1. `health_check`
2. `list_markets` with `{ "view": "simplified", "mode": "sampling" }`
3. `search` with `{ "q": "election", "limit": 5 }`
4. `get_prices` with `{ "items": [{ "token_id": "", "side": "BUY" }] }`
5. read `polymarket://status` if your client supports resources
This verifies stdio transport, canonical market discovery, pluralized market-data tools, and runtime status visibility.
## Local development only: run from source
Use a source checkout only when you need to inspect or modify the server locally.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
git clone https://github.com/CarbonCopyInc/polymarket-mcp.git
cd polymarket-mcp
npm install
npm run build
node dist/index.js
```
For active development:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm run dev
```
Use source checkout for local iteration, not as the default end-user install path.
## Next steps
* [Polymarket MCP Overview](/polymarket-mcp/overview)
* [Capabilities Reference](/polymarket-mcp/capabilities-reference)
* [Workflow: Market Analysis](/polymarket-mcp/workflows-market-analysis)
# Workflow: Market Analysis
Source: https://docs.carboncopy.inc/polymarket-mcp/workflows-market-analysis
Agent-friendly workflow to analyze a Polymarket market before execution using the canonical MCP surface.
## Goal
Produce a repeatable, machine-friendly market brief before placing a trade:
* current market state
* liquidity and spread context
* execution impact estimate
* explicit go/no-go and constraints
## Inputs
* `condition_id` (required)
* optional thesis horizon (`intraday`, `1d`, `1w`, `event_horizon`)
* planned side (`BUY` or `SELL`)
* intended size in USDC
## Recommended tool sequence
### 1) Pull market and event context
* `get_market` with `condition_id`
* `get_event` from the related event id when available
Capture status, outcomes, timing, and event risk windows.
### 2) Pull batched market data
Use the canonical pluralized tools:
* `get_order_books`
* `get_prices`
* `get_midpoints`
* `get_spreads`
* `get_last_trade_prices`
Example:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"tool": "get_prices",
"input": {
"items": [{ "token_id": "", "side": "BUY" }]
}
}
```
Focus on:
* spread regime (tight/normal/wide)
* visible depth near midpoint
* last trade versus current best price
### 3) Estimate execution impact
Run `calculate_market_price` for the intended side and amount.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"tool": "calculate_market_price",
"input": {
"token_id": "",
"side": "BUY",
"amount": "250"
}
}
```
Use the result to set max slippage and a candidate limit price.
### 4) Gather execution metadata
* `get_fee_rate_bps`
* `get_tick_size`
* `get_neg_risk`
These values should flow directly into downstream order validation and execution policy.
### 5) If execution is approved, hand off to trading wrappers
* `prepare_order` to locally prepare/sign a draft without posting
* `place_order` with `confirm: true` only at the final execution step
* `cancel_orders` if you need an explicit unwind/cancel path
## Suggested output contract
Have the agent return a strict structure like:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"thesis": "short rationale",
"confidence": 0.0,
"market_state": {
"status": "active|closed|...",
"best_price": 0.0,
"midpoint": 0.0,
"spread": 0.0,
"last_trade": 0.0
},
"execution": {
"side": "BUY",
"amount_usdc": "250",
"estimated_price": 0.0,
"max_slippage_bps": 0,
"suggested_limit_price": 0.0
},
"risks": ["liquidity", "event timing"],
"go_no_go": "GO|NO_GO",
"fallback_plan": "what to do if liquidity degrades"
}
```
## Optional: use the built-in prompt
You can bootstrap this workflow with prompt `market-analysis` using:
* `condition_id`
* `horizon`
* `include_orderbook_depth`
* `include_recent_trades`
The prompt is registered by the server and already encodes the same analysis sequence.
# Quickstart
Source: https://docs.carboncopy.inc/quickstart
Make your first Carboncopy API call in under 5 minutes.
## Overview
This guide walks you through:
1. Creating a Carboncopy account
2. Generating an API key
3. Making your first request
4. Iterating from there
***
## Step 1 — Create an account
Head to [carboncopy.inc](https://www.carboncopy.inc) and sign in with your email. Once authenticated, connect your Polymarket wallet under [**Settings**](https://www.carboncopy.inc/settings).
***
## Step 2 — Generate an API key
Navigate to [**Settings → API Keys**](https://www.carboncopy.inc/settings) in the Dashboard, then click **New Key**.
Configure the scopes you need — for a read-only monitoring bot, `portfolio:read` and `traders:read` are sufficient. For a bot that can follow/unfollow traders, also add `traders:write`.
The key secret (`cc_<64 hex chars>`) is shown **exactly once** at creation time. Copy it to your password manager or secrets store immediately.
***
## Step 3 — Make your first request
Verify the API is reachable (no auth required):
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://carboncopy.inc/api/v1/health
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"status": "ok",
"version": "1.0.0",
"timestamp": 1741600000000
}
```
Now fetch your portfolio summary using your new key:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export CC_API_KEY="cc_your_key_here"
curl https://carboncopy.inc/api/v1/portfolio \
-H "Authorization: Bearer $CC_API_KEY"
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"totalValue": 5420.75,
"positionsValue": 1840.22,
"currentExposure": 1840.22,
"grossVolume": 7392.10,
"totalVolume": 7392.10,
"realizedPnl": 355.20,
"unrealizedPnl": 65.30,
"totalPnl": 420.50,
"pnlPercentage": 8.41,
"openPositions": 12,
"tradersFollowing": 7
}
```
***
## Step 4 — Follow a trader
To start copying a wallet, your key needs `traders:write` scope:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://carboncopy.inc/api/v1/traders \
-H "Authorization: Bearer $CC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"walletAddress": "0xAbCd1234...",
"copyPercentage": 25,
"maxCopyAmount": 500
}'
```
This will copy 25% of every trade by `0xAbCd1234...`, up to a maximum of \$500 USDC per trade.
***
## Step 5 — Monitor your positions
Poll open positions to track what's currently deployed:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://carboncopy.inc/api/v1/portfolio/positions?limit=20" \
-H "Authorization: Bearer $CC_API_KEY"
```
See [Pagination](/pagination) for iterating beyond the first page.
***
## Next steps
Learn about API key scopes and security.
Full interactive endpoint documentation.
Handle errors gracefully in your bot.
Iterate over large result sets with cursors.
# Rate Limits
Source: https://docs.carboncopy.inc/rate-limits
Understand Carboncopy's rate limit tiers, response headers, and how to handle 429 errors gracefully.
## Overview
Rate limits are enforced per API key. Different endpoint categories have different limits suited to their usage patterns.
| Tier | Limit | Burst | Applies to |
| -------------- | ---------- | ------------ | ----------------------------------------------- |
| **Read** | 60 req/min | +10 | Portfolio, traders (GET), orders (GET), account |
| **Write** | 20 req/min | +5 | Traders (POST, PATCH, DELETE), orders (write) |
| **Key create** | 10 req/min | none (fixed) | `POST /api/v1/keys` |
| **Key read** | 30 req/min | +5 | `GET /api/v1/keys` |
Limits use a **token bucket** algorithm. The burst allowance lets you absorb short spikes above the sustained rate, then the bucket refills at the base rate.
***
## Rate limit headers
Every response includes headers so you can monitor your consumption:
| Header | Description |
| ----------------------- | -------------------------------------------------------- |
| `X-RateLimit-Limit` | Total requests allowed per minute for this endpoint tier |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `Retry-After` | Seconds to wait (only present on `429` responses) |
Example response headers:
```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
```
***
## Handling 429 responses
When you exceed a rate limit, the API returns `429 Too Many Requests`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Retry after 30 seconds."
}
}
```
The `Retry-After` header tells you exactly how long to wait:
```
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
```
### Retry strategy
Implement **exponential backoff with jitter** rather than a fixed sleep:
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import time
import random
import requests
def request_with_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 30))
# Add jitter to avoid thundering herd
sleep_time = retry_after + random.uniform(0, 5)
print(f"Rate limited. Waiting {sleep_time:.1f}s (attempt {attempt + 1})")
time.sleep(sleep_time)
continue
return response
raise Exception("Max retries exceeded")
```
```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
async function requestWithBackoff(url: string, headers: HeadersInit, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, { headers });
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "30", 10);
const jitter = Math.random() * 5;
const sleepMs = (retryAfter + jitter) * 1000;
console.log(`Rate limited. Waiting ${sleepMs}ms`);
await new Promise((r) => setTimeout(r, sleepMs));
continue;
}
return response;
}
throw new Error("Max retries exceeded");
}
```
***
## Tips to stay within limits
* **Paginate efficiently** — fetch larger pages (up to 100 records) instead of many small requests.
* **Cache aggressively** — portfolio summaries and trader lists don't change every second.
* **Use `since`/`until`** — filter by time range to avoid re-fetching old data.
* **One key per service** — avoids different services competing for the same key's budget.
* **Monitor `X-RateLimit-Remaining`** — slow down proactively before hitting 0.