$PRIVY is live -

Skip to content
Docs

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. 1

    Create the buy

    POST /trade/buys with the mint, SOL amount and an idempotencyKey. You get an intent plus an unsigned deposit transaction.

  2. 2

    Sign in your wallet

    Deserialize the base64 transaction and sign it with the owner wallet. Privy never sees your private key.

  3. 3

    Submit the deposit

    POST /trade/buys/{id}/deposit with the signed transaction. Privy checks it pays exactly the intent amount to the Privy treasury before broadcasting it.

  4. 4

    Poll

    GET /trade/buys/{id} every few seconds until status is completed, refunded, failed or expired. The deposit must confirm within 10 minutes.

Buy with @solana/web3.js
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. 1

    Create an authorization

    POST /trade/sells with the mint, raw amount and an idempotencyKey. Privy quotes the sell and returns a message stating the token, amount and minimum SOL you accept.

  2. 2

    Sign the message

    Sign the exact UTF-8 bytes of message with the owner wallet (ed25519) and base58-encode the signature. Do this within 2 minutes.

  3. 3

    Authorize

    POST /trade/sells/{id}/authorize with the signature. Privy verifies it, uses it exactly once, and starts the sell.

  4. 4

    Poll

    GET /trade/sells/{id} until sell.status is terminal. SOL is paid to your main wallet.

Sign a sell authorization
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 with PRICE_MOVED and 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 idempotencyKey returns the original trade instead of creating a second one.

Create a buy

POST/api/v1/trade/buystrade:intents

Locks 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

  • intentobject

    The buy intent.

    • idstring · uuid

      Buy intent ID.

    • statusstring

      Lifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.

    • outputMintstring

      Token being bought.

    • amountLamportsstring

      Total SOL you deposit, in lamports, including fees.

    • minimumOutputAmountstring

      Fewest raw token units the swap may return.

    • depositExpiresAtstring · date-time

      Deposit must confirm before this time.

  • depositobject

    Unsigned deposit for the owner wallet to sign.

    • transactionstring

      Base64 unsigned versioned transaction.

    • lastValidBlockHeightinteger

      Submit before this block height.

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"}'

Try it

Sends a real request
Example response
{
  "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

POST/api/v1/trade/buys/{id}/deposittrade:intents

Send 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 · pathrequired

    Buy intent ID.

Response

  • intentobject

    Updated buy intent.

    • idstring · uuid

      Buy intent ID.

    • statusstring

      Lifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.

    • outputMintstring

      Token being bought.

    • amountLamportsstring

      Total SOL you deposit, in lamports, including fees.

    • minimumOutputAmountstring

      Fewest raw token units the swap may return.

    • depositExpiresAtstring · date-time

      Deposit must confirm before this time.

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…"}'

Try it

Sends a real request
Example response
{
  "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

GET/api/v1/trade/buys/{id}trade:intents

Poll every few seconds after submitting the deposit. Reading also advances confirmation checks.

Parameters

  • idstring · pathrequired

    Buy intent ID.

Response

  • intentobject

    The buy intent.

    • idstring · uuid

      Buy intent ID.

    • statusstring

      Lifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.

    • outputMintstring

      Token being bought.

    • amountLamportsstring

      Total SOL you deposit, in lamports, including fees.

    • minimumOutputAmountstring

      Fewest raw token units the swap may return.

    • depositExpiresAtstring · date-time

      Deposit must confirm before this time.

curl "https://privy.ac/api/v1/trade/buys/3f6c1a2b-9d8e-4f70-a1b2-c3d4e5f60718" \
  -H "X-API-Key: $PRIVY_API_KEY"

Try it

Sends a real request
Example response
{
  "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

POST/api/v1/trade/buys/{id}/canceltrade:intents

Only intents still awaiting a deposit can be cancelled.

Parameters

  • idstring · pathrequired

    Buy intent ID.

Response

  • intentobject

    The cancelled intent.

    • idstring · uuid

      Buy intent ID.

    • statusstring

      Lifecycle status: awaiting_deposit, deposit_submitted, deposit_confirmed, swap_submitted, completed, expired, cancelled, refunded or failed.

    • outputMintstring

      Token being bought.

    • amountLamportsstring

      Total SOL you deposit, in lamports, including fees.

    • minimumOutputAmountstring

      Fewest raw token units the swap may return.

    • depositExpiresAtstring · date-time

      Deposit must confirm before this time.

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 '{}'

Try it

Sends a real request
Example response
{
  "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

POST/api/v1/trade/sellstrade:intents

Quotes 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

  • authorizationobject

    The sell authorization.

    • idstring

      Authorization ID (sell_…).

    • statusstring

      Authorization status.

      awaiting_signatureauthorizedexpiredcancelledfailed

    • mintstring

      Token being sold.

    • amountstring

      Raw token units to sell.

    • positionIdstring · uuidnullable

      Position the sell is limited to, or null for any open position.

    • expectedOutputLamportsstring

      Quoted SOL proceeds in lamports.

    • minimumOutputLamportsstring

      Signed floor. The sell never executes below this.

    • slippageBpsinteger

      Slippage tolerance used for the minimum.

    • messagestring

      Exact UTF-8 text the owner wallet must sign.

    • expiresAtstring · date-time

      Sign and submit before this time.

    • authorizedAtstring · date-timenullable

      When the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.

    • signatureVerifiedAtstring · date-timenullable

      Same value as authorizedAt, named for what it means.

    • executionStartedboolean

      True once the sell passed its checks and was handed to execution. Follow sell.status for progress.

    • failureCodestringnullable

      Stable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.

    • failureReasonstringnullable

      Why the sell could not start.

    • createdAtstring · date-time

      When the authorization was created.

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"}'

Try it

Sends a real request
Example response
{
  "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

POST/api/v1/trade/sells/{id}/authorizetrade:intents

Submit 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 · pathrequired

    Sell authorization ID.

Response

  • authorizationobject

    The authorization, now authorized.

    • idstring

      Authorization ID (sell_…).

    • statusstring

      Authorization status.

      awaiting_signatureauthorizedexpiredcancelledfailed

    • mintstring

      Token being sold.

    • amountstring

      Raw token units to sell.

    • positionIdstring · uuidnullable

      Position the sell is limited to, or null for any open position.

    • expectedOutputLamportsstring

      Quoted SOL proceeds in lamports.

    • minimumOutputLamportsstring

      Signed floor. The sell never executes below this.

    • slippageBpsinteger

      Slippage tolerance used for the minimum.

    • messagestring

      Exact UTF-8 text the owner wallet must sign.

    • expiresAtstring · date-time

      Sign and submit before this time.

    • authorizedAtstring · date-timenullable

      When the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.

    • signatureVerifiedAtstring · date-timenullable

      Same value as authorizedAt, named for what it means.

    • executionStartedboolean

      True once the sell passed its checks and was handed to execution. Follow sell.status for progress.

    • failureCodestringnullable

      Stable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.

    • failureReasonstringnullable

      Why the sell could not start.

    • createdAtstring · date-time

      When the authorization was created.

  • sellobject

    The sell order group, as in Get a sell.

    • statusstring

      Overall 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"}'

Try it

Sends a real request
Example response
{
  "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

GET/api/v1/trade/sells/{id}trade:intents

Poll after authorizing. Returns the authorization and, once authorized, the sell order with its swap and payout progress.

Parameters

  • idstring · pathrequired

    Sell authorization ID.

Response

  • authorizationobject

    The sell authorization.

    • idstring

      Authorization ID (sell_…).

    • statusstring

      Authorization status.

      awaiting_signatureauthorizedexpiredcancelledfailed

    • mintstring

      Token being sold.

    • amountstring

      Raw token units to sell.

    • positionIdstring · uuidnullable

      Position the sell is limited to, or null for any open position.

    • expectedOutputLamportsstring

      Quoted SOL proceeds in lamports.

    • minimumOutputLamportsstring

      Signed floor. The sell never executes below this.

    • slippageBpsinteger

      Slippage tolerance used for the minimum.

    • messagestring

      Exact UTF-8 text the owner wallet must sign.

    • expiresAtstring · date-time

      Sign and submit before this time.

    • authorizedAtstring · date-timenullable

      When the owner signature was verified. It stays set if the sell then fails its pre-execution checks; use executionStarted and failureCode.

    • signatureVerifiedAtstring · date-timenullable

      Same value as authorizedAt, named for what it means.

    • executionStartedboolean

      True once the sell passed its checks and was handed to execution. Follow sell.status for progress.

    • failureCodestringnullable

      Stable error code when status is failed, e.g. QUOTE_BELOW_AUTHORIZED_MINIMUM. A failed authorization never sold anything and never locks the position.

    • failureReasonstringnullable

      Why the sell could not start.

    • createdAtstring · date-time

      When the authorization was created.

  • sellobjectnullable

    The sell order group once authorized.

    • statusstring

      Overall sell status.

curl "https://privy.ac/api/v1/trade/sells/sell_8e2f4a6c1b3d5e7f9a0b2c4d" \
  -H "X-API-Key: $PRIVY_API_KEY"

Try it

Sends a real request
Example response
{
  "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"
}