Wallet marketplace
Browse the Wallet Marketplace, order wallets, pay for them and follow the order from code.
These calls do what the Wallet Marketplace page does in the app: list the wallets for sale, reserve the ones you pick, and hand them to you once you pay. Reads need the read scope. Making an order and checking it need trade.
| Call | Scope | What it does |
|---|---|---|
GET /marketplace/inventory | read | The wallets for sale. |
GET /marketplace/inventory/stats | read | A summary of one wallet type. |
POST /marketplace/orders | trade | Reserve wallets and get a payment address. |
GET /marketplace/orders/:id | read | One order, with your wallets once paid. |
POST /marketplace/orders/:id/check | trade | Look for your payment now. |
GET /marketplace/orders | read | Your last 50 orders. |
Making an order only reserves wallets. You then pay from any wallet you like, in the app, an exchange or your own code. Because the API sends nothing, a key's max per buy, daily cap and wallet list do not apply to these calls.
How a purchase goes#
- List the wallets for sale with
GET /marketplace/inventory. - Pick the ones you want. Note each one's
idand add up theirpriceRaw. That sum is yourexpectedTotal. - Make a request key: a fresh UUID of your own.
- Send
POST /marketplace/orderswith the ids, the total and the request key. The wallets are now held for you for 30 minutes. - Send
amountRawtopaymentAddress, on the order's chain, beforeexpiresAt. - Follow the order with
GET /marketplace/orders/:iduntilstatusisfulfilled. The answer then lists your new wallets.
The payment is an on-chain transfer and cannot be undone. Pay the exact amountRaw, in one payment, before expiresAt. See Real funds & confirms.
Browse the wallets for sale#
GET /marketplace/inventory lists wallets you can buy now, 100 per page. Every query field is optional. A filter you leave out matches every value, unless it has a default.
| Field | What it takes |
|---|---|
chain | solana or robinhood. Default solana. |
kind | standard or dev. Default standard. |
product | funded or aged. Leave it out for both, and for dev wallets. |
tier | basic or premium. |
traderProfile | us, eu or asia. |
proTrader | pro or regular. |
provider | The exchange that first funded the wallet. See below. |
minAgeDays | 0 to 3650. Only wallets funded at least this many days ago. Default 0. |
sort | oldest or newest funding first. Default oldest. |
offset | 0 to 100000. Skip this many wallets. Default 0. |
q | Search text, up to 64 characters. See below. |
provider takes binance, kucoin, bitget, bybit, gate, husher, changenow, sideshift, or unknown for not recorded.
q searches the exchange name, the listing reference (like BIN-20260917-001), # plus the id, and the funding day.
An unknown field gets 400 validation_failed.
The wallet types in the app map to these fields:
| In the app | Query |
|---|---|
| Funded Solana | chain=solana&kind=standard&product=funded |
| Aged Solana | chain=solana&kind=standard&product=aged |
| Aged Robinhood | chain=robinhood&kind=standard&product=aged |
| Solana dev | chain=solana&kind=dev |
| Robinhood dev | chain=robinhood&kind=dev |
Pro traders and Regular are proTrader=pro and proTrader=regular. Pro traders are aged Solana wallets only.
curl -s "https://api.vortexdeployer.com/api/v1/marketplace/inventory?chain=solana&kind=standard&product=aged&sort=newest" \
-H "Authorization: Bearer vd_1a2b3c4d_…"The answer:
{
"items": [
{
"id": "1042",
"chain": "solana",
"kind": "standard",
"product": "aged",
"proTrader": false,
"tier": "basic",
"traderProfile": "eu",
"fundingMode": "cex",
"fundedAt": "2026-09-17T08:12:00.000Z",
"ageDays": 32,
"balanceRaw": "41000000",
"usdcRaw": "0",
"priceRaw": "318000000",
"includedBalanceRaw": "41000000",
"warmupsDone": 0,
"swapsDone": 14,
"swapsTarget": 14,
"fundingProvider": "binance",
"blockedByLineage": false
}
],
"minAgeDays": 0,
"total": 213,
"providers": [ { "provider": "binance", "count": 120 }, { "provider": "unknown", "count": 93 } ],
"devPricing": "twice_preparation_cost",
"devCostMultiplier": 2,
"agedMarkupBps": { "regular": 2800, "proTrader": 4400 }
}id— the listing id. You send it when you order.priceRaw— the full price of this wallet in base units: lamports on Solana, wei on Robinhood. It already includes the balance inside.nullmeans the wallet cannot be priced right now. Skip it.includedBalanceRaw— the coins and USDC already in the wallet, counted in the chain's coin. They become yours. The wallet's own cost ispriceRawminus this.balanceRaw,usdcRaw— the SOL or ETH and the USDC inside, each in its own base units.fundedAt,ageDays— when the wallet was first funded, and how long ago.fundingProvider— the exchange that first funded it.nullmeans not recorded. See funding source.swapsDone,swapsTarget— trades made so far. An aged wallet has both above 0.blockedByLineage—truewhen it shares a funding source with a wallet you bought before. You cannot buy it.total— how many wallets match. Fetch the next page withoffset=100, thenoffset=200.providers— how many matching wallets each exchange funded.
Prices move with the SOL or ETH rate. A wallet with USDC inside is priced at today's rate.
This list does not take limit. A limit gets 400 validation_failed.
The wallet addresses stay hidden until you buy.
Summary of one wallet type#
GET /marketplace/inventory/stats takes chain, kind, tier and product, as above:
{
"chain": "solana",
"kind": "standard",
"tier": null,
"updatedAt": "2026-10-19T12:00:00.000Z",
"count": 213,
"balanceRaw": { "avg": "39000000", "min": "1000000", "max": "120000000" },
"swaps": { "avg": 12.4, "min": 8, "max": 20 },
"fundingAgeDays": { "avg": 21.7, "min": 2.1, "max": 61.3 }
}With nothing for sale, count is 0 and the other figures are null.
Make an order#
POST /marketplace/orders:
{
"chain": "solana",
"ids": ["1042", "1043"],
"expectedTotal": "636000000",
"requestKey": "5b0e2a52-8a3f-4c1d-9a51-0f3c2b7d6e10"
}chain—solanaorrobinhood. Every id must be on this chain.ids— the listing ids you picked. At least one, each once.expectedTotal— the sum of thepriceRawyou saw, as a whole number in a string.requestKey— a UUID you make for this order. See below.
No other field is allowed.
curl -s -X POST https://api.vortexdeployer.com/api/v1/marketplace/orders \
-H "Authorization: Bearer vd_1a2b3c4d_…" \
-H "Content-Type: application/json" \
-d '{
"chain": "solana",
"ids": ["1042", "1043"],
"expectedTotal": "636000000",
"requestKey": "5b0e2a52-8a3f-4c1d-9a51-0f3c2b7d6e10"
}'It answers 201 with the order:
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"chain": "solana",
"status": "pending",
"amountRaw": "636000000",
"receivedRaw": "0",
"paymentAddress": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"expiresAt": "2026-10-19T12:30:00.000Z",
"collectedAt": null,
"items": [
{ "id": "1042", "address": null, "kind": "standard", "priceRaw": "318000000", "fundedAt": "2026-09-17T08:12:00.000Z", "metadata": {}, "warmupsDone": 0, "walletId": null },
{ "id": "1043", "address": null, "kind": "standard", "priceRaw": "318000000", "fundedAt": "2026-09-18T10:40:00.000Z", "metadata": {}, "warmupsDone": 0, "walletId": null }
]
}amountRaw is what you pay. The server prices the wallets again when you order. If its total is within about 1% of your expectedTotal, the order is made at the server's total. If not, you get 409 "The quote changed". List the wallets again and use the new prices.
Request key: safe retries#
The requestKey is how a retry stays safe. Send the same requestKey again and you get the same order back. No second order is made, even if the first answer never reached you. Use a new requestKey for each new order.
These calls take no Idempotency-Key header. The requestKey does that job. Leave the header off …/check in particular: with it, a repeat would return the first, saved answer instead of the order's status now.
Rules for an order#
- One open order at a time. While an order is
pending, a new one gets409"Finish your pending marketplace order first". Pay it, or wait until it expires. - One wallet per funding source, ever. Wallets first funded from the same wallet can be bought together, in one order. Once you have bought one of them, the others are refused in later orders. Check
blockedByLineagebefore you pick. - Funding gap. The app's Quick select keeps your picks a set time apart, so they do not look related. The API does not check this. If you want it, compare
fundedAtyourself. - Sandbox keys cannot order. An order has no dry run, so a sandbox key gets
403 sandbox_key. It can still browse.
When an order is refused#
| Answer | What to do |
|---|---|
400 validation_failed | The body is wrong. Fix what details names. |
403 insufficient_scope | The key lacks trade. Use a key that has it. |
403 sandbox_key | The key is a sandbox key. Use a key without sandbox. |
409 conflict: "Finish your pending marketplace order first" | Pay or wait out your open order. |
409 conflict: "One or more wallets are no longer available. Refresh the marketplace." | Someone bought them first, or an id is wrong. List again and pick again. |
409 conflict: "One or more wallets share a funding source with one of your previous purchases" | Pick only wallets whose blockedByLineage is false. |
409 conflict: "The quote changed. Refresh prices before purchasing." | List again and send the new total. |
All four 409 answers share the code conflict. Tell them apart by the error text.
Pay#
Send exactly amountRaw to paymentAddress:
- in the chain's own coin: SOL on Solana, ETH on Robinhood. Not USDC.
amountRawis in lamports on Solana and in wei on Robinhood.- before
expiresAt, 30 minutes after the order was made. - in one payment, from any wallet: a wallet in your account, an exchange or your own.
A payment a little short, up to about $1, still counts. An exchange's withdrawal fee can take more than that, so check what will arrive.
When expiresAt passes unpaid, the wallets go back on sale. Never send to that payment address again.
Follow the order#
GET /marketplace/orders/:id answers the same order shape as above. The marketplace looks for payments every 15 seconds by itself. Ask every 15 to 30 seconds until the status changes.
POST /marketplace/orders/:id/check looks for your payment right away, then answers the order. Call it once after you pay, to skip the wait. It is also how you report a late payment on an expired order.
Each check counts as a read toward your rate limit, the same as a GET. Making an order does not count.
status is one of:
status | What it means |
|---|---|
pending | Waiting for your payment. In the app: Awaiting payment. |
fulfilled | Paid. The wallets are yours. In the app: Delivered. |
expired | Time ran out with no payment. The wallets went back on sale. In the app: Expired. |
refund_required | Paid too late or too short. Ask Support with the order id. In the app: Needs review. |
receivedRaw— what has arrived at the payment address so far.collectedAt— when the marketplace moved your payment on. You can ignore it.
An order id that is not yours, or not a UUID, gets 404 not_found.
Where your wallets land#
When an order is fulfilled, each item gets its address and walletId. The wallets are now in your account, in your wallet pool:
- Each order gets its own new folder, Marketplace # plus the first 8 characters of the order
id, for example Marketplace #1a2b3c4d. If you already have a folder with that name, the full order id is used. If neither works, the wallets arrive outside any folder. - Dev wallets get the
devrole. All others gettrading. - Each wallet is labelled "Marketplace" plus its kind and listing id, for example "Marketplace standard #1042".
Find them with GET /wallets, like any other wallet. See Wallets, portfolio & PnL. A new wallet's balance may take a moment to show.
No call returns a bought wallet's private key. To export keys, use the app with your password. See Export a private key.
An item's metadata holds notes from when the wallet was prepared. Do not build on it: its fields can change.
Your orders#
GET /marketplace/orders answers your last 50 orders, newest first:
{
"orders": [
{ "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "chain": "solana", "status": "fulfilled", "amountRaw": "636000000", "createdAt": "2026-10-19T12:00:00.000Z" }
]
}It takes ?limit= and ?offset=. X-Total-Count holds the full count. See Paging.
What stays in the app#
- The marketplace operator's admin view.
- The launch info files dev wallets were made with.
See also#
- Wallet Marketplace — the same thing in the app.
- Wallets, portfolio & PnL
- Limits, retries & errors
- MCP for AI agents