Skip to main content
The DFlow Trade API provides a unified interface for trading tokens across both spot and prediction markets on Solana. The Trade API offers a simple workflow with a single endpoint to request orders, automatically handling both synchronous (atomic) and asynchronous (multi-transaction) execution.
This quickstart assumes familiarity with Solana’s transaction and network connection logic. If unfamiliar, please refer to the Solana Cookbook.
For production use, you’ll need an API key to avoid rate limits. See the API Keys guide for details on obtaining and using an API key.
1

Request an Order

The Trade API’s GET /order endpoint returns a quote and open transaction in a single request.
const SOL = "So11111111111111111111111111111111111111112";
const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

/// Amount of SOL to trade to USDC
const amount = 1_000_000_000;

/// Slippage tolerance in bps
const slippageBps = 50;

/// Base URL for the DFlow Trade API
const API_BASE_URL = "https://quote-api.dflow.net";
const API_KEY = process.env.DFLOW_API_KEY; // Optional

const queryParams = new URLSearchParams();
queryParams.append("inputMint", SOL);
queryParams.append("outputMint", USDC);
queryParams.append("amount", amount.toString());
queryParams.append("slippageBps", slippageBps.toString());
queryParams.append("userPublicKey", keypair.publicKey.toBase58());

const headers: HeadersInit = {};
if (API_KEY) {
  headers["x-api-key"] = API_KEY;
}

const orderResponse = await fetch(
  `${API_BASE_URL}/order?${queryParams.toString()}`,
  { headers }
).then((x) => x.json());
2

Sign and Submit the Transaction

Deserialize the transaction, sign it with your keypair, and submit it to Solana using your RPC connection.
/// Deserialize the transaction from base64
const transactionBuffer = Buffer.from(orderResponse.transaction, "base64");
const transaction = VersionedTransaction.deserialize(transactionBuffer);

/// Sign the transaction
transaction.sign([keypair]);

/// Send the transaction to Solana
const signature = await connection.sendTransaction(transaction);
3

Monitor Order Status

How you monitor order completion depends on the executionMode returned from the order request:Sync trades execute atomically in a single transaction. Use standard RPC confirmation.Async trades execute across multiple transactions. Use the /order-status endpoint to poll for completion.
For synchronous trades that execute atomically, use standard Solana transaction confirmation:
if (orderData.executionMode === "sync") {
  /// Monitor transaction status using getSignatureStatuses
  let status;

  do {
    const statusResult = await connection.getSignatureStatuses([signature]);
    status = statusResult.value[0];

    if (!status) {
      console.log("Waiting for transaction confirmation...");
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }
  } while (
    !status ||
    status.confirmationStatus === "processed" ||
    status.confirmationStatus === "confirmed"
  );

  /// Check if transaction succeeded or failed
  if (status.err) {
    console.error("Transaction failed:", status.err);
  } else {
    console.log(`Trade completed successfully in slot ${status.slot}`);
  }
}
For asynchronous trades that execute across multiple transactions, poll the /order-status endpoint:
if (orderData.executionMode === "async") {
  let status;
  let fills = [];

  do {
    /// Poll the order status endpoint
    const headers: HeadersInit = {};
    if (API_KEY) {
      headers["x-api-key"] = API_KEY;
    }
    const statusResponse = await fetch(
      `${API_BASE_URL}/order-status?signature=${signature}`,
      { headers }
    );
    const statusData = await statusResponse.json();

    status = statusData.status;
    fills = statusData.fills || [];

    console.log(`Order status: ${status}`);

    /// Wait before polling again if order is still open
    if (status === "open" || status === "pendingClose") {
      await new Promise((resolve) => setTimeout(resolve, 2000));
    }
  } while (status === "open" || status === "pendingClose");

  /// Process final status
  switch (status) {
    case "closed": {
      if (fills.length > 0) {
        console.log(`Trade completed`);
      } else {
        console.log("Order was closed without any fills");
      }
      break;
    }
    case "pendingClose": {
      if (fills.length > 0) {
        console.log(`Trade ready to close`);
      } else {
        console.log("Order is ready to close without any fills");
      }
      break;
    }
    case "failed": {
      console.log("Order failed to execute");
      break;
    }
  }
}