Subchapter 2.21
plugins/opensea.mdMarkdown19 KBView on GitHub
[!IMPORTANT] Complete the short Base MCP onboarding flow defined in
SKILL.mdbefore calling any OpenSea endpoint. The user’s wallet address — passed tosend_calls— is fetched lazily when needed viaget_wallets.
[!IMPORTANT] All OpenSea API endpoints require authentication. See below — one POST request gives your agent instant access.
## AuthOpenSea is an NFT marketplace and token trading platform. This plugin covers three capabilities: token swaps (cross-chain DEX aggregator), NFT drops and minting, and NFT marketplace trading (buy, sell, cross-chain fulfill via Seaport). The plugin fetches unsigned calldata from the OpenSea REST API or CLI and submits it through Base MCP’s send_calls.
All OpenSea API endpoints require an x-api-key header. This is the first step before using any tool.
Create a free-tier API key instantly with a single call — no signup, no wallet, no human needed:
export OPENSEA_API_KEY=$(curl -s -X POST https://api.opensea.io/api/v2/auth/keys | jq -r '.api_key')Response shape:
{
"api_key": "a1b2c3d4e5f6...",
"name": "agent_free_e753a54c",
"expires_at": "2026-05-14T00:00:00Z",
"rate_limits": { "read": "60/m", "write": "5/m", "fulfillment": "5/m" }
}Use the returned key in the X-API-KEY header on all subsequent requests.
Limits:
To upgrade to higher rate limits, visit https://opensea.io/settings/developer (opens in a new tab).
Full documentation: https://docs.opensea.io/reference/api-keys#instant-api-key-for-agents (opens in a new tab)
Or set an existing key:
export OPENSEA_API_KEY="your-api-key"The CLI reads OPENSEA_API_KEY from the environment automatically.
If a shell is available, prefer the CLI path. Otherwise, call the OpenSea REST API at api.opensea.io directly. No external MCP installation is required.
All endpoints use base URL https://api.opensea.io/api/v2. All require the x-api-key header.
| Method | Path | Purpose |
|---|---|---|
| POST | /auth/keys | Create instant API key (no auth needed for this endpoint) |
| GET | /collections/{slug} | Collection details |
| GET | /collections/{slug}/stats | Collection stats (floor, volume) |
| GET | /listings/collection/{slug}/best | Best listings for collection |
| GET | /listings/collection/{slug}/all | All active listings |
| GET | /offers/collection/{slug}/best | Best offers for collection |
| GET | /chain/{chain}/contract/{address}/nfts/{id} | NFT details |
| GET | /search?query=&type= | Search across collections, NFTs, tokens, accounts |
| POST | /listings/fulfillment_data | Same-chain listing fulfillment (returns decoded struct — see note) |
| POST | /offers/fulfillment_data | Same-chain offer fulfillment (returns decoded struct — see note) |
| POST | /listings/cross_chain_fulfillment_data | Buy listing with any token (returns ready-to-use calldata) |
| GET | /swap/quote?from_chain=&from_address=&to_chain=&to_address=&quantity=&address= | Swap quote with calldata |
| GET | /drops?type=upcoming&chains= | List drops (type: featured, upcoming, recently_minted) |
| GET | /drops/{slug} | Drop details, stages, eligibility |
| POST | /drops/{slug}/mint | Build mint transaction |
| GET | /tokens/trending | Trending tokens |
| GET | /tokens/top | Top tokens by volume |
All GET endpoints support TOON (Token-Optimized Object Notation) — ~40% fewer tokens than JSON. Opt in with Accept: text/markdown:
curl "https://api.opensea.io/api/v2/collections/doodles-official" \
-H "x-api-key: $OPENSEA_API_KEY" \
-H "Accept: text/markdown"Recommended for agents operating within limited context windows.
[!NOTE] For NFT purchases, prefer the cross-chain fulfillment endpoint (
/listings/cross_chain_fulfillment_data) even for same-chain buys. It returns ready-to-use{chain, to, data, value}transactions. The same-chain endpoint (/listings/fulfillment_data) returns a decoded Seaport struct that requires ABI encoding — only use it from the CLI which handles encoding internally.
The CLI runs via npx with no install step required:
# Use without installing:
npx @opensea/cli@latest collections get boredapeyachtclub
# Or install globally:
npm install -g @opensea/cliNo MCP installation is required. This plugin uses direct HTTP calls to api.opensea.io.
# Get swap quote with calldata
opensea swaps quote \
--from-chain base --from-address 0x0000000000000000000000000000000000000000 \
--to-chain base --to-address <token_address> \
--quantity <human_amount> --address <wallet_address>Use 0x0000000000000000000000000000000000000000 for native ETH. The CLI auto-converts human-readable amounts (e.g. 0.02) to smallest units.
REST alternative:
GET /api/v2/swap/quote?from_chain=base&from_address=0x0000000000000000000000000000000000000000&to_chain=base&to_address=<token>&quantity=<amount_in_wei>&address=<wallet>Note: The REST API quantity parameter expects the amount in smallest units (e.g. wei for ETH: 20000000000000000 for 0.02 ETH). The CLI accepts human-readable amounts.
Swap quote response shape:
{
"quote": {
"total_price_usd": 47.40,
"total_cost_usd": 47.90,
"slippage_tolerance": 0.01,
"estimated_duration_ms": 30000,
"price_impact": { "usd": "-0.50", "percent": "-1.05" },
"costs": [{ "type": "GAS", "usd": "0.01" }]
},
"transactions": [{
"chain": "base",
"to": "0xSwapRouter",
"data": "0x...",
"value": "20000000000000000",
"value_hex": "0x470de4df820000"
}]
}Each transaction in transactions[] contains {chain, to, data, value, value_hex}. Use value_hex (already a 0x-prefixed hex string) when passing value to send_calls.
# List drops
opensea drops list --type upcoming --chains base,ethereum
# Get drop details and eligibility
opensea drops get <collection_slug>
# Build mint transaction
opensea drops mint <slug> --minter <wallet_address> --quantity <n>REST alternatives:
GET /api/v2/drops?type=upcoming&chains=base,ethereumGET /api/v2/drops/{slug}POST /api/v2/drops/{slug}/mint (body: { "minter": "<address>", "quantity": <n> })Mint response shape:
{
"to": "0xContractAddress",
"data": "0x...",
"value": "0x...",
"chain": "base"
}The mint endpoint returns to, data, value (hex string), and chain — ready to map directly to send_calls.
# Search collections
opensea search "<query>" --types collection
# Collection stats
opensea collections stats <collection_slug>
# NFT details
opensea nfts get <chain> <contract_address> <token_id>
# Best listing for an NFT
opensea listings best-for-nft <collection_slug> <token_id>
# Best offer for an NFT
opensea offers best-for-nft <collection_slug> <token_id>
# All listings for a collection
opensea listings all <collection_slug> --limit 20[!IMPORTANT] Use the cross-chain fulfillment endpoint for all purchases — it returns ready-to-use hex calldata and works for both same-chain and cross-chain buys. The same-chain endpoint (
/listings/fulfillment_data) returns decoded Seaport structs requiring ABI encoding.
Buy an NFT (using cross-chain endpoint — recommended for all purchases):
curl -s -X POST "https://api.opensea.io/api/v2/listings/cross_chain_fulfillment_data" \
-H "x-api-key: $OPENSEA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"listings": [{
"hash": "<order_hash>",
"chain": "<listing_chain>",
"protocol_address": "0x0000000000000068f116a894984e2db1123eb395"
}],
"fulfiller": { "address": "<buyer_wallet_address>" },
"payment": { "chain": "<payment_chain>", "address": "0x0000000000000000000000000000000000000000" }
}'Set payment.chain to the listing chain and payment.address to the native token for same-chain purchases. For cross-chain, specify a different chain or token address (e.g. USDC).
Cross-chain fulfillment response shape:
{
"transactions": [
{ "chain": "base", "to": "0x...", "data": "0x...", "value": "20000000000000000", "value_hex": "0x470de4df820000" },
{ "chain": "ethereum", "to": "0x...", "data": "0x...", "value": "1000000000000000000", "value_hex": "0xde0b6b3a7640000" }
]
}Transactions are ordered — execute them sequentially. May include approval, bridge, and fulfill steps.
Sell an NFT (accept offer) — shell only:
curl -s -X POST "https://api.opensea.io/api/v2/offers/fulfillment_data" \
-H "x-api-key: $OPENSEA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"offer": {
"hash": "<offer_order_hash>",
"chain": "<chain>",
"protocol_address": "0x0000000000000068f116a894984e2db1123eb395"
},
"fulfiller": { "address": "<seller_wallet_address>" },
"consideration": {
"asset_contract_address": "<nft_contract>",
"token_id": "<token_id>"
}
}'[!NOTE] The same-chain fulfillment endpoints (
/listings/fulfillment_data,/offers/fulfillment_data) return a decoded Seaport struct (function+input_data) — NOT hex calldata. The CLI handles ABI encoding internally. On chat-only surfaces without a CLI, use the cross-chain endpoint for buys (which returns ready-to-use calldata). Selling (accepting offers) currently requires the CLI path.
[!IMPORTANT] The
send_callstool expectsvalueas a hex string (e.g."0x470de4df820000"). The OpenSea API now returns avalue_hexfield alongside the decimalvaluein swap and fulfillment responses. Always usevalue_hexwhen passingvaluetosend_calls.If
value_hexis null or missing, fall back to converting the decimalvaluestring to hex using a code execution tool or shell command. Do NOT compute this conversion mentally — LLMs frequently get large-number hex conversions wrong (e.g. converting4600000000000to0x42E52B800instead of the correct0x42F055DB000), which causes transactions to revert withInsufficientNativeTokensSupplied.Exception: The mint endpoint (
/drops/{slug}/mint) returnsvaluealready as a hex string. No conversion needed for mint.
transactions from the response (swap and fulfillment wrap them in {"transactions": [...]}; mint returns a single flat object).send_calls:
value_hex if present and non-null.value_hex is null/missing: convert value from decimal to hex (see fallback methods below)."0x0".send_calls using to, data, and the hex value.Only needed if value_hex is null or missing:
In shell: printf "0x%x" 20000000000000000 outputs 0x470de4df820000
In JavaScript: "0x" + BigInt("20000000000000000").toString(16) outputs "0x470de4df820000"
In Python: hex(20000000000000000) outputs "0x470de4df820000"
1. get_wallets → address
2. Create API key if not already set (POST /api/v2/auth/keys)
3. opensea swaps quote --from-chain <chain> --from-address <from> \
--to-chain <chain> --to-address <to> \
--quantity <amount> --address <address>
(or GET /api/v2/swap/quote?from_chain=...&quantity=<amount_in_wei>&...)
4. Review quote with user: check price_impact, costs, total_price_usd
5. Use `value_hex` from each transaction (see Value Conversion)
6. For each transaction:
send_calls(chain=<transaction.chain>, calls=[{to, value: value_hex, data}])
7. User approves → get_request_status(requestId)1. get_wallets → address
2. Create API key if not already set (POST /api/v2/auth/keys)
3. opensea drops list --chains <chains> --type upcoming
(or GET /api/v2/drops?type=upcoming&chains=<chains>)
4. opensea drops get <slug> → check eligibility, pricing, supply
(or GET /api/v2/drops/{slug})
5. Confirm mint with user (price, quantity)
6. opensea drops mint <slug> --minter <address> --quantity <n>
(or POST /api/v2/drops/{slug}/mint with body {"minter":"<addr>","quantity":<n>})
7. send_calls(chain=<response.chain>, calls=[{to: response.to, value: response.value, data: response.data}])
(mint value is already hex — no conversion needed)
8. User approves → get_request_status(requestId)1. get_wallets → address
2. Create API key if not already set (POST /api/v2/auth/keys)
3. opensea search "<query>" --types collection → find collection
(or GET /api/v2/search?query=<query>&type=collections)
4. opensea listings best-for-nft <slug> <token_id> → get order_hash, price
(or GET /api/v2/listings/collection/{slug}/best)
5. Confirm price with user
6. POST /api/v2/listings/cross_chain_fulfillment_data with:
listings=[{hash, chain, protocol_address}], fulfiller={address}, payment={chain, address}
(works for same-chain and cross-chain)
7. Use `value_hex` from each transaction (see Value Conversion)
8. For each transaction (in order):
send_calls(chain=<transaction.chain>, calls=[{to, value: value_hex, data}])
9. User approves → get_request_status(requestId)1. get_wallets → address
2. Create API key if not already set (POST /api/v2/auth/keys)
3. opensea offers best-for-nft <slug> <token_id> → get offer_hash, price
4. Confirm acceptance with user
5. Use CLI to handle fulfillment (CLI encodes Seaport calldata internally)
6. send_calls(chain=<chain>, calls from CLI output)
7. User approves → get_request_status(requestId)Target tool: send_calls
All OpenSea write operations produce unsigned transaction data. Use value_hex from the API response when passing value to send_calls (see Value Conversion). Exception: mint returns value already as hex.
Swap — use value_hex from each response.transactions[] entry:
{
"chain": "<transaction.chain>",
"calls": [{
"to": "<transaction.to>",
"value": "<transaction.value_hex>",
"data": "<transaction.data>"
}]
}If multiple transactions, submit them in order (each may be on a different chain).
Buy (cross-chain fulfillment) — use value_hex from each response.transactions[] entry, iterate in order:
{
"chain": "<transaction.chain>",
"calls": [{
"to": "<transaction.to>",
"value": "<transaction.value_hex>",
"data": "<transaction.data>"
}]
}Transactions may span multiple chains (e.g. approval on Base, then fulfill on Ethereum). Submit each send_calls in sequence, waiting for confirmation before the next.
Mint — map response directly (value is already hex):
{
"chain": "<response.chain>",
"calls": [{
"to": "<response.to>",
"value": "<response.value>",
"data": "<response.data>"
}]
}Swap 0.02 ETH for USDC on BasePOST /api/v2/auth/keys (if not already set).get_wallets.opensea swaps quote --from-chain base --from-address 0x0000000000000000000000000000000000000000 --to-chain base --to-address 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 --quantity 0.02 --address <address> (or GET /api/v2/swap/quote with quantity=20000000000000000).value_hex from each transaction as value in send_calls.Buy a Bored Ape on EthereumPOST /api/v2/auth/keys (if not already set).get_wallets.opensea listings best boredapeyachtclub --limit 5 to show cheapest listings.order_hash./api/v2/listings/cross_chain_fulfillment_data with listing hash, fulfiller, and payment (ETH on ethereum).value_hex from each transaction as value; submit each via send_calls in order.What drops are coming up on Base?POST /api/v2/auth/keys (if not already set).opensea drops list --chains base --type upcoming (or GET /api/v2/drops?type=upcoming&chains=base).opensea drops mint <slug> --minter <address> (or POST to /api/v2/drops/{slug}/mint).send_calls (mint value is already hex).Buy an NFT on Ethereum using USDC from BasePOST /api/v2/auth/keys (if not already set).get_wallets.opensea listings best-for-nft <slug> <token_id>./api/v2/listings/cross_chain_fulfillment_data with payment {chain: "base", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"}.value_hex from each transaction as value; submit each via send_calls in order (approval on Base → bridge → fulfill on Ethereum).price_impact and costs. Always present these to the user before submitting. If price_impact.percent exceeds 5%, warn the user explicitly. Do not auto-raise slippage tolerance.send_calls. Never auto-buy.send_calls parameters.| Chain | Base MCP string | chainId |
|---|---|---|
| Ethereum | ethereum | 1 |
| Base | base | 8453 |
| Polygon | polygon | 137 |
| Arbitrum | arbitrum | 42161 |
| Optimism | optimism | 10 |
| Avalanche | avalanche | 43114 |
The OpenSea API and Base MCP both use polygon as the chain identifier for Polygon.
Seaport 1.6 address (all chains): 0x0000000000000068F116a894984e2DB1123eB395
USDC on Base: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Native ETH address: 0x0000000000000000000000000000000000000000