DocsAPI
New

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.

CallScopeWhat it does
GET /marketplace/inventoryreadThe wallets for sale.
GET /marketplace/inventory/statsreadA summary of one wallet type.
POST /marketplace/orderstradeReserve wallets and get a payment address.
GET /marketplace/orders/:idreadOne order, with your wallets once paid.
POST /marketplace/orders/:id/checktradeLook for your payment now.
GET /marketplace/ordersreadYour last 50 orders.
An order moves no funds

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#

  1. List the wallets for sale with GET /marketplace/inventory.
  2. Pick the ones you want. Note each one's id and add up their priceRaw. That sum is your expectedTotal.
  3. Make a request key: a fresh UUID of your own.
  4. Send POST /marketplace/orders with the ids, the total and the request key. The wallets are now held for you for 30 minutes.
  5. Send amountRaw to paymentAddress, on the order's chain, before expiresAt.
  6. Follow the order with GET /marketplace/orders/:id until status is fulfilled. The answer then lists your new wallets.
You pay with real SOL or ETH

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.

FieldWhat it takes
chainsolana or robinhood. Default solana.
kindstandard or dev. Default standard.
productfunded or aged. Leave it out for both, and for dev wallets.
tierbasic or premium.
traderProfileus, eu or asia.
proTraderpro or regular.
providerThe exchange that first funded the wallet. See below.
minAgeDays0 to 3650. Only wallets funded at least this many days ago. Default 0.
sortoldest or newest funding first. Default oldest.
offset0 to 100000. Skip this many wallets. Default 0.
qSearch 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 appQuery
Funded Solanachain=solana&kind=standard&product=funded
Aged Solanachain=solana&kind=standard&product=aged
Aged Robinhoodchain=robinhood&kind=standard&product=aged
Solana devchain=solana&kind=dev
Robinhood devchain=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. null means 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 is priceRaw minus 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. null means not recorded. See funding source.
  • swapsDone, swapsTarget — trades made so far. An aged wallet has both above 0.
  • blockedByLineage — true when it shares a funding source with a wallet you bought before. You cannot buy it.
  • total — how many wallets match. Fetch the next page with offset=100, then offset=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 — solana or robinhood. Every id must be on this chain.
  • ids — the listing ids you picked. At least one, each once.
  • expectedTotal — the sum of the priceRaw you 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 gets 409 "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 blockedByLineage before 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 fundedAt yourself.
  • 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#

AnswerWhat to do
400 validation_failedThe body is wrong. Fix what details names.
403 insufficient_scopeThe key lacks trade. Use a key that has it.
403 sandbox_keyThe 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.
  • amountRaw is 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.

After the order expires, do not pay

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:

statusWhat it means
pendingWaiting for your payment. In the app: Awaiting payment.
fulfilledPaid. The wallets are yours. In the app: Delivered.
expiredTime ran out with no payment. The wallets went back on sale. In the app: Expired.
refund_requiredPaid 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 dev role. All others get trading.
  • 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#