Skip to main content
The Express Verification API lets you submit tokens for Jupiter VRFD verification and update token metadata programmatically. This is the same Express flow available on the VRFD UI, exposed as an API for launchpads, agents, and projects that need to integrate verification into their token creation pipelines. Each Express submission costs 1000 JUP, which prevents spam and prioritizes requests. You can pay in JUP directly, or in SOL, USDC, or JUPUSD, which are converted to 1000 JUP via Ultra. Standard submissions on the VRFD site are free. You can request verification and metadata updates together. Each is reviewed independently, so a metadata update can proceed even if verification is declined.

Payment Currencies

Pass paymentCurrency as a token symbol (for example SOL), not a mint address. JUP is the default and transfers directly. SOL, USDC, and JUPUSD are converted to 1000 JUP via an Ultra swap.
paymentCurrency valueResolves to mintDecimalsConversion
JUPJUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN6Direct transfer of 1000 JUP
SOLSo111111111111111111111111111111111111111129Swapped to 1000 JUP via Ultra
USDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v6Swapped to 1000 JUP via Ultra
JUPUSDJuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD6Swapped to 1000 JUP via Ultra

Prerequisites

  1. Get an API key at Portal. All requests require the x-api-key header.
  2. The submitting wallet needs enough of your chosen payment currency for 1000 JUP. Transactions are gasless, so the wallet does not need extra SOL for network fees.

Flow

The API is a three-step flow: check eligibility, craft a payment transaction, sign it, then execute.

Step 1: Check Eligibility (Optional)

Check if the token can be submitted for verification and/or metadata updates. This step is optional since the execute step also rejects ineligible tokens before charging payment, but it keeps your flow clean and avoids unnecessary transaction crafting. The most common reason for ineligibility is an existing pending submission for the token.
const response = await fetch(
  'https://api.jup.ag/tokens/v2/verify/express/check-eligibility?tokenId=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  {
    headers: { 'x-api-key': 'YOUR_API_KEY' },
  }
);
const eligibility = await response.json();
{
  "tokenExists": true,
  "isVerified": false,
  "canVerify": true,
  "canMetadata": false,
  "metadataError": "A premium metadata update request for this token already exists"
}
FieldTypeDescription
tokenExistsbooleanWhether the token exists on-chain
isVerifiedbooleanWhether the token is already verified
canVerifybooleanWhether a verification request can be submitted
canMetadatabooleanWhether a metadata update can be submitted
verificationErrorstringWhy verification cannot proceed (only present when there is a specific error)
metadataErrorstringWhy metadata cannot proceed (only present when there is a specific error)
If both canVerify and canMetadata are false, the submission will be rejected before any payment is charged.

Step 2: Craft the Payment Transaction

This returns an unsigned Solana transaction worth 1000 JUP. By default it crafts a direct JUP transfer. Pass paymentCurrency to pay in SOL, USDC, or JUPUSD instead, which crafts an Ultra swap to JUP (see Pay with SOL, USDC, or JUPUSD). Save the requestId from the response, you need it in the next step.
const craftResponse = await fetch(
  `https://api.jup.ag/tokens/v2/verify/express/craft-txn?senderAddress=${walletAddress}`,
  {
    headers: { 'x-api-key': 'YOUR_API_KEY' },
  }
);
const { transaction, requestId } = await craftResponse.json();
ParameterTypeRequiredDescription
senderAddressstringYesSolana wallet address that will sign and pay for the transaction
paymentCurrencystringNoToken symbol, not a mint address: JUP (default), SOL, USDC, or JUPUSD. Non-JUP currencies route through an Ultra swap to JUP.
The response includes the base64-encoded unsigned transaction and a requestId that links this payment to your execute request.
{
  "receiverAddress": "5x...recipient",
  "mint": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
  "amount": "1000000000",
  "tokenDecimals": 6,
  "tokenUsdRate": 0.42,
  "feeLamports": 5000,
  "feeUsdAmount": 0.0009,
  "feeMint": "So11111111111111111111111111111111111111112",
  "feeTokenDecimals": 9,
  "feeAmount": 5000,
  "transaction": "AQAB...",
  "requestId": "req_abc123",
  "totalTime": 412,
  "expireAt": "2026-06-30T12:00:00.000Z",
  "code": 0,
  "gasless": true
}
FieldTypeDescription
transactionstringBase64-encoded unsigned transaction
requestIdstringUnique ID for this payment. Pass to /execute.
receiverAddressstringWallet address receiving the JUP payment
mintstringMint of the JUP collected (always JUP)
amountstringJUP amount in smallest unit (1000 JUP = 1000000000)
tokenDecimalsnumberDecimals of the JUP collected (6)
tokenUsdRatenumberUSD rate of JUP at craft time
feeLamportsnumberNetwork fee for the transaction, in lamports
feeUsdAmountnumberNetwork fee in USD
feeMintstringMint address of the fee token
feeTokenDecimalsnumberDecimals of the fee token
feeAmountnumberFee amount in the fee token’s smallest unit
totalTimenumberTime taken to craft the transaction, in milliseconds
expireAtstringExpiry timestamp for the crafted transaction
codenumberStatus code (0 for success)
gaslessbooleanWhether the transaction is gasless
Non-JUP payments return additional swap sizing fields. See Pay with SOL, USDC, or JUPUSD.

Step 3: Sign and Execute

Sign the transaction from Step 2 with your wallet, then submit it along with the token details and metadata.
import { VersionedTransaction } from '@solana/web3.js';

// Deserialize and sign the transaction
const txBuffer = Buffer.from(transaction, 'base64');
const tx = VersionedTransaction.deserialize(txBuffer);
tx.sign([wallet]); // wallet is a Keypair

// Serialize the signed transaction back to base64
const signedTransaction = Buffer.from(tx.serialize()).toString('base64');

// Submit
const executeResponse = await fetch(
  'https://api.jup.ag/tokens/v2/verify/express/execute',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      transaction: signedTransaction,
      requestId,
      senderAddress: wallet.publicKey.toBase58(),
      tokenId: 'YOUR_TOKEN_MINT_ADDRESS',
      twitterHandle: 'https://x.com/yourproject',
      description: 'Submitting for verification because...',
      tokenMetadata: {
        tokenId: 'YOUR_TOKEN_MINT_ADDRESS',
        website: 'https://yourproject.io',
        twitter: 'https://x.com/yourproject',
        discord: 'https://discord.gg/yourproject',
      },
    }),
  }
);
const result = await executeResponse.json();
FieldTypeRequiredDescription
transactionstringYesBase64-encoded signed transaction from craft-txn
requestIdstringYesrequestId from the craft-txn response
senderAddressstringYesSolana wallet address of the sender
tokenIdstringYesToken mint address to verify
twitterHandlestringYesTwitter/X handle of the token project
descriptionstringYesReason for the verification request
senderTwitterHandlestringNoTwitter/X handle of the submitter
tokenMetadataobjectNoToken metadata to submit alongside verification. See Token Metadata Fields.
paymentCurrencystringNoToken symbol, not a mint address: JUP (default), SOL, USDC, or JUPUSD. Must match the currency used in craft-txn.
paymentAmountstringConditionalAtomic input amount paid, from the craft response’s quotedInputAmount. Required when paymentCurrency is not JUP.
jupOutputAmountstringNoAtomic JUP output from the craft response (its amount field). Optional, but recommended on non-JUP paths so revenue tracks the JUP actually collected.
{
  "status": "Success",
  "signature": "5UfDuX...",
  "totalTime": 1234,
  "verificationCreated": true,
  "metadataCreated": true
}
FieldTypeDescription
status"Success" | "Failed"Whether the transaction executed
signaturestringOn-chain transaction signature (present on success)
errorstringError message (present on failure)
codenumberError code (present on failure)
totalTimenumberTime taken to execute, in milliseconds
verificationCreatedbooleanWhether a verification request was created
metadataCreatedbooleanWhether a metadata update request was created
After submission, track your request status at verified.jup.ag/tokens/browse.

Pay with SOL, USDC, or JUPUSD

To pay with a non-JUP currency, set paymentCurrency on both the craft and execute steps. The craft step returns an Ultra swap that converts your input token into JUP, sized with a small buffer so the swap yields at least 1000 JUP. Request the transaction with your chosen currency:
const craftResponse = await fetch(
  `https://api.jup.ag/tokens/v2/verify/express/craft-txn?senderAddress=${walletAddress}&paymentCurrency=SOL`,
  {
    headers: { 'x-api-key': 'YOUR_API_KEY' },
  }
);
const craft = await craftResponse.json();
On non-JUP paths the response includes these swap sizing fields in addition to the common fields above:
FieldTypeDescription
inputMintstringInput token mint address
inputDecimalsnumberInput token decimals
quotedInputAmountstringAtomic input amount for the swap, sized to yield at least 1000 JUP
maxInputAmountstringMaximum atomic input amount for the swap. Equal to quotedInputAmount on the current swap flow.
When you execute, pass paymentAmount (required on non-JUP paths) and jupOutputAmount (optional, for revenue tracking). paymentAmount is the input amount from the craft response’s quotedInputAmount, and jupOutputAmount is the craft response’s amount:
body: JSON.stringify({
  transaction: signedTransaction,
  requestId,
  senderAddress: wallet.publicKey.toBase58(),
  tokenId: 'YOUR_TOKEN_MINT_ADDRESS',
  twitterHandle: 'https://x.com/yourproject',
  description: 'Submitting for verification because...',
  paymentCurrency: 'SOL',
  paymentAmount: craft.quotedInputAmount,
  jupOutputAmount: craft.amount,
}),

Token Metadata Fields

The tokenMetadata object in the execute request is optional. Include it to update metadata alongside your verification submission. Only tokenId is required, all other fields are optional.
FieldTypeDescription
tokenIdstringToken mint address (required)
namestringToken display name
symbolstringToken ticker symbol
iconstringURL to token icon image
websitestringProject website URL
twitterstringTwitter/X profile URL
twitterCommunitystringTwitter/X community URL
telegramstringTelegram group URL
discordstringDiscord invite URL
instagramstringInstagram profile URL
tiktokstringTikTok profile URL
tokenDescriptionstringShort description of the token
circulatingSupplystringCirculating supply value
useCirculatingSupplybooleanWhether to use the provided circulatingSupply value
circulatingSupplyUrlstringURL to a live circulating supply endpoint
useCirculatingSupplyUrlbooleanWhether to use the provided circulatingSupplyUrl
coingeckoCoinIdstringCoinGecko coin ID for price data
useCoingeckoCoinIdbooleanWhether to use the provided coingeckoCoinId
otherUrlstringAny other relevant URL

Known Projects and Launchpads

If you are a known project or launchpad on Solana, DM @jup_vrfd from your organization account on X. This links your Developer Platform OrgId to your submissions, which helps the VRFD team review them.

API Reference

Check Eligibility

Check if a token can be verified

Craft Transaction

Get an unsigned 1000 JUP payment transaction

Execute

Submit signed transaction with token details