Marketplace API
Overview
The CollectorCrypt Marketplace V2 API lets you build clients on top of the CollectorCrypt V2 marketplace program on Solana. Every request follows the same three-step pattern:
- Build — POST to a builder endpoint (
/marketplace/list,/buy, etc.) to receive an unsigned transaction. If the wallet has no SOL for fees, the backend can co-sign as fee payer and return a partially-signed transaction (seeuserHasSol). - Sign — the user's wallet signs the transaction.
- Broadcast — POST the signed transaction to
/marketplace/broadcast.
- Program ID:
CcmRKTuZCGJBWQwMHvDYApBRvSZNHqGJXkznqpDTSQUr - Currency: USDC (SPL Token)
- NFT types: pNFTs (Programmable NFTs) and cNFTs (Compressed NFTs)
Base URL
| Environment | Base URL |
|---|---|
| Production | https://api.collectorcrypt.com |
| Devnet | https://dev-api.collectorcrypt.com |
All endpoints below are relative to the base URL.
Devnet USDC faucet
For testing on devnet: https://spl-token-faucet.com/?token-name=USDC-Dev
Request format
All endpoints accept JSON bodies and include a required wallet field — the caller's Solana wallet address (base58). The backend builds an unsigned transaction addressed to that wallet; the wallet's signature on the returned transaction is what authorizes the action on-chain.
curl -X POST "https://api.collectorcrypt.com/marketplace/list" \
-H "Content-Type: application/json" \
-d '{ "wallet": "YOUR_WALLET_ADDRESS", "cardId": "...", "nftAddress": "...", "price": 25.00, "currency": "USDC" }'
Endpoints
1. List an NFT — POST /marketplace/list
Creates a transaction for listing an NFT for sale. The seller must sign and broadcast the transaction via /marketplace/broadcast.
Request Body:
{
"wallet": "SELLER_WALLET_ADDRESS",
"cardId": "card_db_id",
"currency": "USDC",
"price": 25.00,
"nftAddress": "NFT_MINT_ADDRESS",
"tokenStandard": "Pnft",
"userHasSol": true
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The seller's wallet public key |
cardId | string | Yes | The CollectorCrypt card ID |
currency | string | Yes | Currency for the listing (use "USDC") |
price | number | Yes | Listing price in USDC |
nftAddress | string | Yes | The NFT mint address |
tokenStandard | "Pnft" | "Cnft" | No | Token standard. Optional; resolved via DAS if omitted. |
userHasSol | boolean | No | Set to false to have the backend act as fee payer (returned transaction is pre-signed by the backend). Default: the backend detects the wallet's SOL balance and caches the decision. |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
Error Responses:
400: On-chain error while building the listing transaction409: NFT is already listed on the marketplace
2. Cancel a Listing — POST /marketplace/cancel-listing
Creates a transaction to cancel an active listing. The seller must sign and broadcast it.
Request Body:
{
"wallet": "CALLER_WALLET_ADDRESS",
"tokenMint": "NFT_MINT_ADDRESS",
"seller": "SELLER_WALLET_ADDRESS"
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The caller's wallet public key (the signer) |
tokenMint | string | Yes | The NFT mint address |
seller | string | No | The listing seller — defaults to wallet if omitted |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
Error Responses:
400: On-chain error while building the cancel transaction404: No active listing found on-chain for this NFT
3. Update a Listing Price — POST /marketplace/update-listing
Creates a transaction to update the price of an active listing.
Request Body:
{
"wallet": "SELLER_WALLET_ADDRESS",
"tokenMint": "NFT_MINT_ADDRESS",
"newPrice": 30.00,
"coin": "USDC"
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The seller's wallet public key |
tokenMint | string | Yes | The NFT mint address |
newPrice | number | Yes | Updated listing price in USDC |
coin | string | Yes | Currency (use "USDC") |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
Error Responses:
400:newPriceequals the current listing price, or on-chain error while building the update transaction404: No listing found on-chain for this NFT
4. Buy an NFT — POST /marketplace/buy
Creates a transaction for purchasing a listed NFT. The buyer must sign and broadcast it.
Request Body:
{
"wallet": "BUYER_WALLET_ADDRESS",
"nftAddress": "NFT_MINT_ADDRESS",
"price": 25.00,
"currency": "USDC",
"tokenStandard": "Pnft",
"sellerWallet": "SELLER_WALLET_ADDRESS",
"fundingSource": "wallet"
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The buyer's wallet public key |
nftAddress | string | Yes | The NFT mint address |
price | number | Yes | The listed price in USDC |
currency | string | Yes | Currency (use "USDC") |
tokenStandard | "Pnft" | "Cnft" | No | Token standard. Optional; resolved via DAS if omitted. |
sellerWallet | string | No | Seller wallet. Optional; resolved from the DB if omitted. |
fundingSource | "wallet" | "escrow" | No | Payment source. Defaults to "wallet". Set to "escrow" to pay from the buyer's on-chain shared escrow balance. |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
When fundingSource is "escrow", the response is a JSON object with two transactions (a withdraw from escrow, then the buy) to sign together:
{
"transactions": ["BASE64_WITHDRAW_TX", "BASE64_BUY_TX"],
"fundingSource": "escrow"
}
Escrow mode requires the buyer's escrow to hold at least price × 1.02 USDC (price + 2% platform fee).
Error Responses:
400: On-chain error, or insufficient escrow balance (escrow mode only)404: Card, seller wallet, or listing not found409: Card owner changed since the listing was built (someone else bought it first) — refetch and retry
5. Make an Offer — POST /marketplace/make-offer
Creates a transaction for placing an offer on an NFT. Funds are moved into the buyer's on-chain escrow account. The buyer must sign and broadcast the transaction.
Request Body:
{
"wallet": "BUYER_WALLET_ADDRESS",
"cardId": "card_db_id",
"nftAddress": "NFT_MINT_ADDRESS",
"price": 20.00,
"currency": "USDC",
"tokenStandard": "Cnft",
"userHasSol": true,
"ownerWallet": "OWNER_WALLET_ADDRESS",
"collectionHash": "MERKLE_TREE_COLLECTION_HASH"
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The buyer's wallet public key |
cardId | string | Yes | The CollectorCrypt card ID |
nftAddress | string | Yes | The NFT mint address |
price | number | Yes | Offer price in USDC |
currency | string | Yes | Currency (use "USDC") |
tokenStandard | "Pnft" | "Cnft" | No | Token standard. Optional; resolved via DAS if omitted. |
userHasSol | boolean | No | Set to false to have the backend act as fee payer (returned transaction is pre-signed by the backend). Default: the backend detects the wallet's SOL balance and caches the decision. |
ownerWallet | string | No | Current NFT owner. Optional; resolved via RPC if omitted. |
collectionHash | string | No | cNFT collection hash. Optional; resolved via DAS if omitted (cNFTs only). |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
Error Responses:
400: Could not determine NFT owner from the chain, or on-chain error while building the offer transaction
6. Cancel an Offer — POST /marketplace/cancel-offer
Creates a transaction to cancel an active offer. Optionally keeps funds in escrow for future offers.
Request Body:
{
"wallet": "BUYER_WALLET_ADDRESS",
"nftAddress": "NFT_MINT_ADDRESS",
"keepInEscrow": false
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The buyer's wallet public key (offer maker) |
nftAddress | string | Yes | The NFT mint address |
keepInEscrow | boolean | No | If true, released USDC stays in your escrow account. If false (default), USDC is withdrawn to your wallet. |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
If the wallet has insufficient SOL for rent, the backend automatically falls back to keepInEscrow: true (funds stay in escrow).
Error Responses:
404: No active offer found on-chain for this buyer/NFT pair
7. Update an Offer — POST /marketplace/update-offer
Creates a transaction to change the price of an existing offer.
Request Body:
{
"wallet": "BUYER_WALLET_ADDRESS",
"nftAddress": "NFT_MINT_ADDRESS",
"currency": "USDC",
"buyer": "BUYER_WALLET_ADDRESS",
"price": 22.00
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The buyer's wallet public key (offer maker, signer) |
nftAddress | string | Yes | The NFT mint address |
currency | string | Yes | Currency (use "USDC") |
buyer | string | Yes | The buyer's wallet public key (typically equal to wallet) |
price | number | Yes | New offer price in USDC |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
The program requires the buyer's escrow to already hold the full new amount — this transaction does not transfer additional USDC into escrow.
Error Responses:
400: New price equals the current offer price, buyer has no escrow account, escrow balance is less than the new price, or on-chain error while building the update transaction404: Offer not found on-chain (the DB record, if any, is markedCancelledin the background)
8. Accept an Offer — POST /marketplace/accept-offer
Creates a transaction for the NFT owner to accept a buyer's offer. The seller must sign and broadcast it.
Request Body:
{
"wallet": "SELLER_WALLET_ADDRESS",
"nftAddress": "NFT_MINT_ADDRESS",
"buyer": "BUYER_WALLET_ADDRESS",
"currency": "USDC",
"price": 20.00,
"tokenStandard": "Pnft",
"userHasSol": true
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The seller's wallet public key (NFT owner, signer) |
nftAddress | string | Yes | The NFT mint address |
buyer | string | Yes | The buyer's wallet public key (the offer maker) |
currency | string | Yes | Currency (use "USDC") |
price | number | Yes | The offer price in USDC |
tokenStandard | "Pnft" | "Cnft" | No | Token standard. Optional; resolved via DAS if omitted. |
userHasSol | boolean | No | Set to false to have the backend act as fee payer (returned transaction is pre-signed by the backend). Default: the backend detects the wallet's SOL balance and caches the decision. |
Response: The unsigned transaction as a base64 string.
BASE64_ENCODED_UNSIGNED_TRANSACTION
If the NFT is a cNFT that's currently listed, the program requires the listing to be cancelled first. In that case the response is a JSON object containing a cancel-listing transaction — broadcast it, then call /marketplace/accept-offer again to get the settlement transaction:
{
"requiresCancelListing": true,
"cancelListingTx": "BASE64_CANCEL_LISTING_TX",
"message": "This cNFT is currently listed. Please sign the cancel listing transaction first, then accept the offer."
}
Error Responses:
400: Offer price on-chain has drifted from the submittedprice(race: the buyer updated or cancelled), or on-chain error while building the accept transaction404: Offer not found on-chain for this buyer/NFT pair
9. Browse Listings — GET /marketplace
Returns a paginated, filterable list of cards on the marketplace. This is a read endpoint — no wallet signature is required. Use it to discover listings (and the offers on them) before calling any of the transaction builders above.
The endpoint is public and lightly cached (5 seconds for unauthenticated requests).
Pagination
| Param | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | 1-indexed page number |
step | integer | 100 | Page size. Max 100. |
Filter parameters (all optional unless noted)
CSV parameters accept either a comma-separated string (?cardType=Card,Sealed) or repeated query keys.
| Param | Type | Description |
|---|---|---|
search | string | Substring match against itemName, nftAddress, gemrateCardName, and gradingID |
ownerAddress | string | Solana wallet — return only cards owned by this address |
hideOwned | boolean | If true and the caller is authenticated, hide the caller's own cards |
favoritesOnly | boolean | Only cards the caller has favorited (requires auth) |
followingOnly | boolean | Only cards owned by users the caller follows (requires auth) |
followingOf | string | Wallet — only cards owned by users that wallet follows |
marketplaceStatus | CSV | Buy now, Has offers — filter by listing/offer state |
marketplaceSource | CSV | CC (CollectorCrypt V2), ME (Magic Eden) |
marketplaceTags | CSV | Filter by one or more global tag names (e.g. ?marketplaceTags=Hot,Featured) |
listPriceMin / listPriceMax | number | Listed price range in USDC (SOL listings are auto-converted) |
insuredValueMin / insuredValueMax | number | Filter by insuredValue |
yearMin / yearMax | integer | Card year range |
categories | CSV | One or more of Baseball, Basketball, Football, Pokemon, Magic The Gathering, Yu-Gi-Oh!, Lorcana, One Piece, Non-Sport, … (see service for the full list) |
cardType | CSV | Card, Comic, ComicRaw, Game, Merch, Raw, Sealed |
blockchain | CSV | Solana, … (Prisma Blockchain enum) |
authenticated | boolean | Only authenticated cards |
autographed | boolean | Only autographed cards |
burnt | boolean | Include burned cards. Default excludes them. |
grade | string | Substring match against grade |
gradeNum filter via genericGradeMin / genericGradeMax | number | Numeric grade range |
gradingCompany | CSV | PSA, Beckett, CGC, SGC, TAG, CSG, KSA, PSA/DNA, UDA, Steiner, BBCE, Rare Edition, Not graded |
set / format / language / productCode | string | Substring match for sealed/comic metadata |
pokemon | string | Word-boundary match against gemrateCardName |
ccBuyback | boolean | Only cards with an active CollectorCrypt buyback offer |
gemrateSet / gemrateGrade / gemrateCardName / gemrateSpecId / gemrateParallel | string | Gemrate-specific substring filters |
orderBy | enum | See below. Default listedDateDesc. |
orderBy values
dateAsc, dateDesc, nameAsc, nameDesc, priceAsc, priceDesc (insured value), yearAsc, yearDesc, listedDateAsc, listedDateDesc, listedPriceAsc, listedPriceDesc.
Example
# First page of Pokémon cards listed on CC for under $100, newest first
curl "https://api.collectorcrypt.com/marketplace?categories=Pokemon&marketplaceSource=CC&listPriceMax=100&orderBy=listedDateDesc&page=1&step=50"
Response
{
"filterNFtCard": [
{
"id": "card_db_id",
"itemName": "...",
"nftAddress": "NFT_MINT_ADDRESS",
"nftStandard": "Pnft",
"blockchain": "Solana",
"category": "Pokemon",
"type": "Card",
"year": 1999,
"grade": "10",
"gradeNum": 10,
"gradingCompany": "PSA",
"gradingID": "...",
"insuredValue": "...",
"status": "Transferred",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-05-10T00:00:00.000Z",
"listing": {
"createdAt": "2026-05-01T00:00:00.000Z",
"updatedAt": "2026-05-01T00:00:00.000Z",
"currency": "USDC",
"price": "25",
"receiptId": "...",
"sellerId": "...",
"marketplace": "CC"
},
"offers": [{ "id": "offer_id" }],
"owner": {
"id": "user_id",
"name": "...",
"wallet": "OWNER_WALLET_ADDRESS"
},
"images": {
"front": "https://...",
"frontM": "https://...",
"frontS": "https://...",
"back": "https://...",
"backM": "https://...",
"backS": "https://..."
}
}
],
"findTotal": 1234,
"total": 50000,
"totalPages": 25,
"cardsQtyByCategory": {
"Pokemon": 800,
"Baseball": 200,
"...": 0
}
}
| Field | Description |
|---|---|
filterNFtCard | The current page of cards. listing is null when the card isn't listed; offers is an array of active-offer ids. |
findTotal | Total cards matching the filter (excludes the categories filter — used to compute cardsQtyByCategory). |
total | Total cards in the marketplace overall (status = 'Transferred'). |
totalPages | ceil(findTotal / step). |
cardsQtyByCategory | Count of matching cards per category — useful for sidebar facet counts. |
10. Broadcast a Transaction — POST /marketplace/broadcast
Broadcasts a signed transaction to the Solana network. Use this after the user signs any transaction returned by the endpoints above.
Request Body:
{
"wallet": "SIGNER_WALLET_ADDRESS",
"signedTransaction": "base64_encoded_signed_transaction",
"nftAddress": "NFT_MINT_ADDRESS"
}
| Field | Type | Required | Description |
|---|---|---|---|
wallet | string | Yes | The wallet that signed the transaction — used for fee-payer cache hints |
signedTransaction | string | Yes | The base64-encoded signed transaction |
nftAddress | string | No | If provided, triggers a background card metadata refresh after 5 seconds |
Response:
{
"success": true,
"signature": "SOLANA_TRANSACTION_SIGNATURE",
"message": "Transaction broadcast successfully"
}
The transaction is validated against an allow-list of program IDs (the CollectorCrypt marketplace program and related system programs) before being submitted.
Retryable insufficient-funds error:
If the signer wallet has insufficient SOL for fees, the endpoint returns 400 with a structured JSON error body in message:
{
"code": "INSUFFICIENT_FUNDS_RETRYABLE",
"message": "Insufficient funds for transaction fees",
"retryable": true,
"useBackendPayer": true
}
Rebuild the transaction by calling the original builder endpoint with userHasSol: false — the backend will set itself as fee payer.
Error Responses:
400: Invalid or undeserializable transaction, insufficient funds (retryable — see above), or submission failure403: Transaction contains a program ID that isn't on the allow-list
Errors
All error responses use this shape:
{
"statusCode": 400,
"message": "human-readable error",
"error": "Bad Request"
}
| Status | Meaning |
|---|---|
400 | Bad request — missing wallet, on-chain failure, price mismatch, insufficient escrow, or invalid signed transaction |
403 | Transaction contains a program ID outside the broadcast allow-list |
404 | Listing, offer, card, or seller wallet not found |
409 | NFT is already listed, or the on-chain owner changed during /buy |
429 | Rate limit exceeded |
For /marketplace/broadcast, the insufficient-funds case returns a JSON-encoded string inside message with code: "INSUFFICIENT_FUNDS_RETRYABLE" — see the broadcast section above.