# 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.