Skip to main content

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:

  1. Build — POST to a builder endpoint (/marketplace/list, /buy, etc.) to receive an unsigned transaction. The backend automatically checks the wallet's on-chain SOL balance and, if it can't cover network fees, co-signs as fee payer and returns a partially-signed transaction.
  2. Sign — the user's wallet signs the transaction.
  3. Broadcast — POST the signed transaction to /marketplace/broadcast.
  • Program ID: CcmRKTuZCGJBWQwMHvDYApBRvSZNHqGJXkznqpDTSQUr
  • Currency: USDC (SPL Token)
  • NFT types: pNFTs (Programmable NFTs), cNFTs (Compressed NFTs), Standard NFTs (legacy Token Metadata 1/1s), and MPL Core assets

Pricing and fees

The buyer always pays exactly the listed price (or, for an offer, the offer amount). A platform fee (currently 2%) is deducted from the seller's proceeds — it is not added on top of the buyer's payment. So for a price sale the buyer pays price, the seller receives price − fee, and the fee goes to the platform treasury. This is the same for direct buys and accepted offers.

Base URL

EnvironmentBase URL
Productionhttps://api.collectorcrypt.com
Devnethttps://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
}
FieldTypeRequiredDescription
walletstringYesThe seller's wallet public key
cardIdstringYesThe CollectorCrypt card ID
currencystringYesCurrency for the listing (use "USDC")
pricenumberYesListing price in USDC
nftAddressstringYesThe NFT mint address
tokenStandard"Pnft" | "Cnft" | "StandardNft" | "CoreNft"NoToken standard. Optional; resolved via DAS if omitted.
userHasSolbooleanNoAdvisory hint only. The backend decides whether to act as fee payer by checking the wallet's on-chain SOL balance (cached briefly); when it does, the returned transaction is pre-signed by the backend.

Response: The unsigned transaction as a base64 string.

BASE64_ENCODED_UNSIGNED_TRANSACTION

Error Responses:

  • 400: On-chain error while building the listing transaction
  • 409: 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"
}
FieldTypeRequiredDescription
walletstringYesThe caller's wallet public key (the signer)
tokenMintstringYesThe NFT mint address
sellerstringNoThe 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 transaction
  • 404: 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"
}
FieldTypeRequiredDescription
walletstringYesThe seller's wallet public key
tokenMintstringYesThe NFT mint address
newPricenumberYesUpdated listing price in USDC
coinstringYesCurrency (use "USDC")

Response: The unsigned transaction as a base64 string.

BASE64_ENCODED_UNSIGNED_TRANSACTION

Error Responses:

  • 400: newPrice equals the current listing price, or on-chain error while building the update transaction
  • 404: 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"
}
FieldTypeRequiredDescription
walletstringYesThe buyer's wallet public key
nftAddressstringYesThe NFT mint address
pricenumberYesThe listed price in USDC
currencystringYesCurrency (use "USDC")
tokenStandard"Pnft" | "Cnft" | "StandardNft" | "CoreNft"NoToken standard. Optional; resolved via DAS if omitted.
sellerWalletstringNoSeller wallet. Optional; resolved from the DB if omitted.
fundingSource"wallet" | "escrow"NoPayment 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 USDC. The buyer pays exactly price; the platform fee is taken from the seller's proceeds (see Pricing and fees).

Error Responses:

  • 400: On-chain error, or insufficient escrow balance (escrow mode only)
  • 404: Card, seller wallet, or listing not found
  • 409: Card owner changed since the listing was built (someone else bought it first) — refetch and retry

5. Make an Offer

An offer must be backed by the buyer's on-chain escrow balance — when the seller accepts, the funds are pulled from escrow (see the Offer flow). Because of this, an offer can be funded in three different ways, and you pick the one that matches the buyer's current escrow balance. This funding choice is the key decision when building an offer.

You want to…UseDepositsReturns
Top up only what's missing, then offer (recommended / what the CollectorCrypt UI uses)createMakeOfferWithDepositTxsmax(0, price − escrowBalance)multiple txs
Use funds already in escrow, no depositcreateMakeOfferTxV2NoDepositnothingsingle tx
Always deposit the full price, then offerPOST /marketplace/make-offer (a.k.a. createMakeOfferTxV2)pricesingle tx

All three move USDC into the buyer's escrow as needed; nothing is paid to the seller until the offer is accepted. The fee model is the same as a buy — on acceptance the seller receives price − fee (see Pricing and fees).

Calling the createMakeOffer* builders. The two escrow-aware variants are exposed through the JSON-RPC-style dispatcher, not as their own REST path: POST /v2 with a body of { "method": "<name>", "params": { ... } }. The response is whatever the method's result is (a base64 string, or an object). All three builders are public — pass wallet in params.

5a. createMakeOfferWithDepositTxs (deposit shortfall + offer)

Computes the escrow shortfall (depositAmount = max(0, price − currentEscrowBalance)), then returns a deposit transaction (only if needed) plus the offer transaction, to sign together with signAllTransactions.

POST /v2

{
"method": "createMakeOfferWithDepositTxs",
"params": {
"wallet": "BUYER_WALLET_ADDRESS",
"cardId": "card_db_id",
"nftAddress": "NFT_MINT_ADDRESS",
"price": 20.00,
"currency": "USDC"
}
}

Response:

{
"transactions": ["BASE64_TX", "BASE64_TX"],
"blockhash": "RECENT_BLOCKHASH",
"depositAmount": 5.00,
"needsDeposit": true
}

When the buyer's escrow already covers price, needsDeposit is false, depositAmount is 0, and only the offer transaction is returned.

5b. createMakeOfferTxV2NoDeposit (use existing escrow)

Builds the offer transaction without depositing. The buyer's escrow must already hold at least price.

POST /v2

{
"method": "createMakeOfferTxV2NoDeposit",
"params": {
"wallet": "BUYER_WALLET_ADDRESS",
"cardId": "card_db_id",
"nftAddress": "NFT_MINT_ADDRESS",
"price": 20.00,
"currency": "USDC",
"tokenStandard": "Cnft",
"ownerWallet": "OWNER_WALLET_ADDRESS",
"escrowBalance": 25.00,
"collectionHash": "MERKLE_TREE_COLLECTION_HASH"
}
}

Response: the unsigned transaction as a base64 string.

Error Responses:

  • 400: no escrow account ("You do not have a shared escrow account…"), or escrow balance < price ("Insufficient escrow balance…"), or could not determine NFT owner, or on-chain build error

5c. POST /marketplace/make-offer (deposit full price)

Single transaction that deposits the full price into escrow and creates the offer. Simplest path, but it always deposits — use createMakeOfferTxV2NoDeposit / createMakeOfferWithDepositTxs if the buyer may already have escrow funds.

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"
}
FieldTypeRequiredDescription
walletstringYesThe buyer's wallet public key
cardIdstringYesThe CollectorCrypt card ID
nftAddressstringYesThe NFT mint address
pricenumberYesOffer price in USDC (also the amount deposited into escrow)
currencystringYesCurrency (use "USDC")
tokenStandard"Pnft" | "Cnft" | "StandardNft" | "CoreNft"NoToken standard. Optional; resolved via DAS if omitted.
userHasSolbooleanNoAdvisory hint only. The backend decides whether to act as fee payer by checking the wallet's on-chain SOL balance (cached briefly); when it does, the returned transaction is pre-signed by the backend.
ownerWalletstringNoCurrent NFT owner. Optional; resolved via RPC if omitted.
collectionHashstringNocNFT collection hash. Optional; resolved via DAS if omitted (cNFTs only).

Response: the unsigned transaction as a base64 string.

Error Responses:

  • 400: Could not determine NFT owner from the chain, or on-chain error while building the offer transaction

Managing escrow directly. You can also fund or drain escrow on its own via the POST /v2 builders createDepositToUserEscrowTx and createWithdrawFromUserEscrowTx (params: { amount, wallet }), each returning a base64 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
}
FieldTypeRequiredDescription
walletstringYesThe buyer's wallet public key (offer maker)
nftAddressstringYesThe NFT mint address
keepInEscrowbooleanNoIf 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
}
FieldTypeRequiredDescription
walletstringYesThe buyer's wallet public key (offer maker, signer)
nftAddressstringYesThe NFT mint address
currencystringYesCurrency (use "USDC")
buyerstringYesThe buyer's wallet public key (typically equal to wallet)
pricenumberYesNew 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. To raise an offer above the current escrow balance, use createUpdateOfferWithDepositTxs (via POST /v2, params { wallet, nftAddress, buyer, price, currency }); like its make-offer counterpart it deposits only the shortfall and returns { transactions, blockhash, depositAmount, needsDeposit }.

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 transaction
  • 404: Offer not found on-chain (the DB record, if any, is marked Cancelled in 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
}
FieldTypeRequiredDescription
walletstringYesThe seller's wallet public key (NFT owner, signer)
nftAddressstringYesThe NFT mint address
buyerstringYesThe buyer's wallet public key (the offer maker)
currencystringYesCurrency (use "USDC")
pricenumberYesThe offer price in USDC
tokenStandard"Pnft" | "Cnft" | "StandardNft" | "CoreNft"NoToken standard. Optional; resolved via DAS if omitted.
userHasSolbooleanNoAdvisory hint only. The backend decides whether to act as fee payer by checking the wallet's on-chain SOL balance (cached briefly); when it does, the returned transaction is pre-signed by the backend.

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 submitted price (race: the buyer updated or cancelled), or on-chain error while building the accept transaction
  • 404: 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 fully public and cached at the CDN (Cache-Control: public, s-maxage=60, max-age=30).

Pagination

ParamTypeDefaultDescription
pageinteger11-indexed page number
stepinteger100Page size. Max 100.

Filter parameters (all optional unless noted)

CSV parameters accept either a comma-separated string (?cardType=Card,Sealed) or repeated query keys.

ParamTypeDescription
searchstringSubstring match against itemName and gemrateCardName; exact match against nftAddress and gradingID
ownerAddressstringSolana wallet — return only cards owned by this address
marketplaceStatusCSVBuy now — only currently-listed cards
marketplaceSourceCSVCC (CollectorCrypt V2), ME (Magic Eden)
marketplaceTagsCSVFilter by one or more global tag names (e.g. ?marketplaceTags=Hot,Featured)
listPriceMin / listPriceMaxnumberListed price range in USDC (SOL listings are auto-converted)
insuredValueMin / insuredValueMaxnumberFilter by insuredValue
yearMin / yearMaxintegerCard year range
categoriesCSVOne or more of Baseball, Basketball, Football, Hockey, Soccer, Golf, Tennis, Racing, Boxing & MMA, Misc Sports, Pokemon, Magic The Gathering, Yu-Gi-Oh!, Lorcana, One Piece, Collectible Card Games, Non-Sport, Azuki, Vibes
cardTypeCSVCard, Comic, ComicRaw, Game, Merch, Raw, Sealed
blockchainCSVSolana, Base, Monad, ApeChain, Ethereum, XLayer
autographedbooleanOnly autographed cards
burntbooleanInclude burned cards. Default excludes them.
gradestringILIKE match against grade (caller supplies any % wildcards)
genericGradeMin / genericGradeMaxnumberNumeric gradeNum range
gradingCompanyCSVPSA, PSA/DNA, Beckett, CGC, CBCS, CGS, CSG, SGC, TAG, KSA, UDA, Steiner, BBCE, Rare Edition, Not graded
set / format / productCodestringSubstring match for sealed/comic metadata
languagestringLanguage filter (e.g. English); matched on word boundaries, not as a plain substring
pokemonstringWord-boundary match against gemrateCardName
ccBuybackbooleanOnly cards with an active CollectorCrypt buyback offer
gemrateCardNamestringSubstring match against gemrateCardName
gemrateGradestring | CSVExact match (or IN list) against the gemrate grade
gemrateSet / gemrateSpecId / gemrateParallelstringSubstring match against the card's set / spec id / parallel
orderByenumSee 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
}
}
FieldDescription
filterNFtCardThe current page of cards. listing is null when the card isn't listed; offers is an array of active-offer ids.
findTotalTotal cards matching the filter (excludes the categories filter — used to compute cardsQtyByCategory).
totalTotal cards in the marketplace overall (status = 'Transferred').
totalPagesceil(findTotal / step).
cardsQtyByCategoryCount 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"
}
FieldTypeRequiredDescription
walletstringYesThe wallet that signed the transaction — used for fee-payer cache hints
signedTransactionstringYesThe base64-encoded signed transaction
nftAddressstringNoIf 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 failure
  • 403: 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"
}
StatusMeaning
400Bad request — missing wallet, on-chain failure, price mismatch, insufficient escrow, or invalid signed transaction
403Transaction contains a program ID outside the broadcast allow-list
404Listing, offer, card, or seller wallet not found
409NFT is already listed, or the on-chain owner changed during /buy
429Rate 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.