API reference
Trading
Create buys and sells from your own client. Every trade is signed by your wallet.
Keys with the trade:intents scope can start buys and sells. The API prepares each trade, but nothing moves until the wallet that owns the key signs it. A leaked key can create unsigned trades that expire unused. It cannot spend your SOL or sell your tokens.
Buying
- 1
Create the buy
POST /trade/buyswith the mint, SOL amount and anidempotencyKey. You get an intent plus an unsigned deposit transaction. - 2
Sign in your wallet
Deserialize the base64 transaction and sign it with the owner wallet. Privy never sees your private key.
- 3
Submit the deposit
POST /trade/buys/{id}/depositwith the signed transaction. Privy checks it pays exactly the intent amount to the Privy treasury before broadcasting it. - 4
Poll
GET /trade/buys/{id}every few seconds untilstatusiscompleted,refunded,failedorexpired. The deposit must confirm within 10 minutes.
import { VersionedTransaction } from "@solana/web3.js"
const api = (path, body) =>
fetch(`https://privy.ac/api/v1${path}`, {
method: body ? "POST" : "GET",
headers: { "X-API-Key": process.env.PRIVY_API_KEY, "Content-Type": "application/json" },
body: body && JSON.stringify(body),
}).then((r) => r.json())
const { data } = await api("/trade/buys", {
mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
amountLamports: "100000000",
idempotencyKey: crypto.randomUUID(),
})
const tx = VersionedTransaction.deserialize(Buffer.from(data.deposit.transaction, "base64"))
tx.sign([ownerKeypair]) // or: await wallet.signTransaction(tx)
await api(`/trade/buys/${data.intent.id}/deposit`, {
signedTransaction: Buffer.from(tx.serialize()).toString("base64"),
})Selling
- 1
Create an authorization
POST /trade/sellswith the mint, rawamountand anidempotencyKey. Privy quotes the sell and returns amessagestating the token, amount and minimum SOL you accept. - 2
Sign the message
Sign the exact UTF-8 bytes of
messagewith the owner wallet (ed25519) and base58-encode the signature. Do this within 2 minutes. - 3
Authorize
POST /trade/sells/{id}/authorizewith the signature. Privy verifies it, uses it exactly once, and starts the sell. - 4
Poll
GET /trade/sells/{id}untilsell.statusis terminal. SOL is paid to your main wallet.
import nacl from "tweetnacl"
import bs58 from "bs58"
const { data } = await api("/trade/sells", {
mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
amount: "250000000000",
idempotencyKey: crypto.randomUUID(),
})
const bytes = new TextEncoder().encode(data.authorization.message)
const signature = bs58.encode(nacl.sign.detached(bytes, ownerKeypair.secretKey))
await api(`/trade/sells/${data.authorization.id}/authorize`, { signature })Safety guarantees
- Owner-signed only. Deposits must come from, and sell signatures must match, the wallet that owns the API key.
- Exact amounts. A deposit that pays a different amount or a different recipient is rejected before it reaches the network.
- Signed minimums. A sell never executes below
minimumOutputLamports. If the price drops further, it fails withPRICE_MOVEDand nothing is sold. - Single use. Each sell authorization works once and expires after 2 minutes. Each buy deposit is accepted once.
- Idempotent. Retrying a create request with the same
idempotencyKeyreturns the original trade instead of creating a second one.
Create a buy
/api/v1/trade/buystrade:intentsLocks a quote, creates a buy intent and returns an unsigned SOL deposit transaction. Nothing moves until the key owner signs that transaction in their own wallet and submits it with Submit deposit.
Response
intentobjectThe buy intent.
idstring · uuidBuy intent ID.
statusstringLifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.
outputMintstringToken being bought.
amountLamportsstringTotal SOL you deposit, in lamports, including fees.
minimumOutputAmountstringFewest raw token units the swap may return.
depositExpiresAtstring · date-timeDeposit must confirm before this time.
depositobjectUnsigned deposit for the owner wallet to sign.
transactionstringBase64 unsigned versioned transaction.
lastValidBlockHeightintegerSubmit before this block height.
Errors
curl -X POST "https://privy.ac/api/v1/trade/buys" \
-H "X-API-Key: $PRIVY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mint":"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263","amountLamports":"100000000","slippageBps":100,"idempotencyKey":"bot-2026-10-10-0001"}'{
"data": {
"intent": {
"id": "3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718",
"status": "awaiting_deposit",
"outputMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amountLamports": "101000000",
"minimumOutputAmount": "412880551234",
"depositExpiresAt": "2026-10-10T12:10:00.000Z"
},
"deposit": {
"transaction": "AQAAAAAAAAAAAAAA…",
"lastValidBlockHeight": 312004511
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Submit deposit
/api/v1/trade/buys/{id}/deposittrade:intentsSend the deposit transaction after the owner wallet signs it. Privy verifies it pays exactly the intent amount to the Privy treasury before broadcasting, then executes the swap once it confirms.
Parameters
idstring · pathrequiredBuy intent ID.
Response
intentobjectUpdated buy intent.
idstring · uuidBuy intent ID.
statusstringLifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.
outputMintstringToken being bought.
amountLamportsstringTotal SOL you deposit, in lamports, including fees.
minimumOutputAmountstringFewest raw token units the swap may return.
depositExpiresAtstring · date-timeDeposit must confirm before this time.
Errors
curl -X POST "https://privy.ac/api/v1/trade/buys/3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718/deposit" \
-H "X-API-Key: $PRIVY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"signedTransaction":"AQAAAAAAAAAAAAAA…"}'{
"data": {
"intent": {
"id": "3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718",
"status": "deposit_submitted",
"outputMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amountLamports": "101000000",
"minimumOutputAmount": "412880551234",
"depositExpiresAt": "2026-10-10T12:10:00.000Z"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Get a buy
/api/v1/trade/buys/{id}trade:intentsPoll every few seconds after submitting the deposit. Reading also advances confirmation checks.
Parameters
idstring · pathrequiredBuy intent ID.
Response
intentobjectThe buy intent.
idstring · uuidBuy intent ID.
statusstringLifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.
outputMintstringToken being bought.
amountLamportsstringTotal SOL you deposit, in lamports, including fees.
minimumOutputAmountstringFewest raw token units the swap may return.
depositExpiresAtstring · date-timeDeposit must confirm before this time.
Errors
curl "https://privy.ac/api/v1/trade/buys/3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718" \
-H "X-API-Key: $PRIVY_API_KEY"{
"data": {
"intent": {
"id": "3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718",
"status": "completed",
"outputMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amountLamports": "101000000",
"minimumOutputAmount": "412880551234",
"depositExpiresAt": "2026-10-10T12:10:00.000Z"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Cancel a buy
/api/v1/trade/buys/{id}/canceltrade:intentsOnly intents still awaiting a deposit can be cancelled.
Parameters
idstring · pathrequiredBuy intent ID.
Response
intentobjectThe cancelled intent.
idstring · uuidBuy intent ID.
statusstringLifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.
outputMintstringToken being bought.
amountLamportsstringTotal SOL you deposit, in lamports, including fees.
minimumOutputAmountstringFewest raw token units the swap may return.
depositExpiresAtstring · date-timeDeposit must confirm before this time.
Errors
curl -X POST "https://privy.ac/api/v1/trade/buys/3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718/cancel" \
-H "X-API-Key: $PRIVY_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"data": {
"intent": {
"id": "3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718",
"status": "cancelled",
"outputMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amountLamports": "101000000",
"minimumOutputAmount": "412880551234",
"depositExpiresAt": "2026-10-10T12:10:00.000Z"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Create a sell authorization
/api/v1/trade/sellstrade:intentsQuotes a sell of tokens Privy holds for you and returns a sell authorization with the exact message the owner wallet must sign. The authorization expires after 2 minutes. Repeating the request with the same idempotencyKey returns the same authorization with status 200.
Response
authorizationobjectThe sell authorization.
idstringAuthorization ID (sell_…).
statusstringAuthorization status.
awaiting_signatureauthorizedexpiredcancelledfailedmintstringToken being sold.
amountstringRaw token units to sell.
positionIdstring · uuidnullablePosition the sell is limited to, or null for any open position.
expectedOutputLamportsstringQuoted SOL proceeds in lamports.
minimumOutputLamportsstringSigned floor. The sell never executes below this.
slippageBpsintegerSlippage tolerance used for the minimum.
messagestringExact UTF-8 text the owner wallet must sign.
expiresAtstring · date-timeSign and submit before this time.
authorizedAtstring · date-timenullableWhen the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.
signatureVerifiedAtstring · date-timenullableSame value as authorizedAt, named for what it means.
executionStartedbooleanTrue once the sell passed its checks and was handed to execution. Follow sell.status for progress.
failureCodestringnullableStable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.
failureReasonstringnullableWhy the sell could not start.
createdAtstring · date-timeWhen the authorization was created.
Errors
curl -X POST "https://privy.ac/api/v1/trade/sells" \
-H "X-API-Key: $PRIVY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mint":"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263","amount":"250000000000","slippageBps":100,"idempotencyKey":"bot-2026-10-10-0001"}'{
"data": {
"authorization": {
"id": "sell_8e2f4a6c1b3d5e7f9a0b2c4d",
"status": "awaiting_signature",
"mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amount": "250000000000",
"positionId": null,
"expectedOutputLamports": "61234567",
"minimumOutputLamports": "60622221",
"slippageBps": 100,
"message": "privy.trade wants you to authorize a sell with your Solana account:\n7xKX…sAsU\n…",
"expiresAt": "2026-10-10T12:02:00.000Z",
"authorizedAt": null,
"signatureVerifiedAt": null,
"executionStarted": false,
"failureCode": null,
"failureReason": null,
"createdAt": "2026-10-10T12:00:00.000Z"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Authorize a sell
/api/v1/trade/sells/{id}/authorizetrade:intentsSubmit the owner wallet’s ed25519 signature over the authorization message. Privy verifies it, consumes the authorization exactly once and starts the sell. It refuses to execute below the signed minimum.
Parameters
idstring · pathrequiredSell authorization ID.
Response
authorizationobjectThe authorization, now authorized.
idstringAuthorization ID (sell_…).
statusstringAuthorization status.
awaiting_signatureauthorizedexpiredcancelledfailedmintstringToken being sold.
amountstringRaw token units to sell.
positionIdstring · uuidnullablePosition the sell is limited to, or null for any open position.
expectedOutputLamportsstringQuoted SOL proceeds in lamports.
minimumOutputLamportsstringSigned floor. The sell never executes below this.
slippageBpsintegerSlippage tolerance used for the minimum.
messagestringExact UTF-8 text the owner wallet must sign.
expiresAtstring · date-timeSign and submit before this time.
authorizedAtstring · date-timenullableWhen the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.
signatureVerifiedAtstring · date-timenullableSame value as authorizedAt, named for what it means.
executionStartedbooleanTrue once the sell passed its checks and was handed to execution. Follow sell.status for progress.
failureCodestringnullableStable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.
failureReasonstringnullableWhy the sell could not start.
createdAtstring · date-timeWhen the authorization was created.
sellobjectThe sell order group, as in Get a sell.
statusstringOverall sell status.
curl -X POST "https://privy.ac/api/v1/trade/sells/sell_8e2f4a6c1b3d5e7f9a0b2c4d/authorize" \
-H "X-API-Key: $PRIVY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"signature":"4vJ9…Qe2"}'{
"data": {
"authorization": {
"id": "sell_8e2f4a6c1b3d5e7f9a0b2c4d",
"status": "authorized",
"mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amount": "250000000000",
"positionId": null,
"expectedOutputLamports": "61234567",
"minimumOutputLamports": "60622221",
"slippageBps": 100,
"message": "privy.trade wants you to authorize a sell with your Solana account:\n7xKX…sAsU\n…",
"expiresAt": "2026-10-10T12:02:00.000Z",
"authorizedAt": "2026-10-10T12:00:41.000Z",
"signatureVerifiedAt": null,
"executionStarted": false,
"failureCode": null,
"failureReason": null,
"createdAt": "2026-10-10T12:00:00.000Z"
},
"sell": {
"status": "swap_pending"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}Get a sell
/api/v1/trade/sells/{id}trade:intentsPoll after authorizing. Returns the authorization and, once authorized, the sell order with its swap and payout progress.
Parameters
idstring · pathrequiredSell authorization ID.
Response
authorizationobjectThe sell authorization.
idstringAuthorization ID (sell_…).
statusstringAuthorization status.
awaiting_signatureauthorizedexpiredcancelledfailedmintstringToken being sold.
amountstringRaw token units to sell.
positionIdstring · uuidnullablePosition the sell is limited to, or null for any open position.
expectedOutputLamportsstringQuoted SOL proceeds in lamports.
minimumOutputLamportsstringSigned floor. The sell never executes below this.
slippageBpsintegerSlippage tolerance used for the minimum.
messagestringExact UTF-8 text the owner wallet must sign.
expiresAtstring · date-timeSign and submit before this time.
authorizedAtstring · date-timenullableWhen the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.
signatureVerifiedAtstring · date-timenullableSame value as authorizedAt, named for what it means.
executionStartedbooleanTrue once the sell passed its checks and was handed to execution. Follow sell.status for progress.
failureCodestringnullableStable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.
failureReasonstringnullableWhy the sell could not start.
createdAtstring · date-timeWhen the authorization was created.
sellobjectnullableThe sell order group once authorized.
statusstringOverall sell status.
Errors
curl "https://privy.ac/api/v1/trade/sells/sell_8e2f4a6c1b3d5e7f9a0b2c4d" \
-H "X-API-Key: $PRIVY_API_KEY"{
"data": {
"authorization": {
"id": "sell_8e2f4a6c1b3d5e7f9a0b2c4d",
"status": "authorized",
"mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"amount": "250000000000",
"positionId": null,
"expectedOutputLamports": "61234567",
"minimumOutputLamports": "60622221",
"slippageBps": 100,
"message": "privy.trade wants you to authorize a sell with your Solana account:\n7xKX…sAsU\n…",
"expiresAt": "2026-10-10T12:02:00.000Z",
"authorizedAt": null,
"signatureVerifiedAt": null,
"executionStarted": false,
"failureCode": null,
"failureReason": null,
"createdAt": "2026-10-10T12:00:00.000Z"
},
"sell": {
"status": "completed"
}
},
"requestId": "req_8f3a2c91d4e6b07f"
}