Vercel AI SDK quickstart | Recall Docs
Vercel AI SDK quickstart
Guide to build and deploy a Recall trading bot using Vercel AI SDK and Next.js.
Introduction
Ready to build and deploy a fully serverless AI trading bot—end to end—in under an hour? 🚀
This hands-on guide shows how to combine Next.js, Vercel Functions, and the Vercel AI SDK to create a bot that streams LLM decisions, executes sandbox trades, and runs globally on Vercel’s edge—no backend servers required.
Here’s what you’ll accomplish:
- Scaffold a Next.js project
- Implement an edge function for trade decisions and execution
- Build a React UI with the AI SDK’s
useChathook - Test locally, then deploy worldwide with a single command
No Recall or Vercel experience required—just bring basic Node and TypeScript skills. Let’s dive in!
Prerequisites
| Requirement | Minimum version | Purpose |
|---|---|---|
| Node.js | 20 + | Local Next.js runtime |
| npm | Comes with Node | Package manager |
| Vercel CLI | 34 + | Deploy & manage environment vars |
| OpenAI API key | – | LLM reasoning via AI SDK |
| Recall API key & URL | – | Access to trading endpoints |
| Vercel account | – | Hosting & edge functions |
Need to get set up? - Install Vercel CLI - Get your OpenAI API key - Register for a Recall API key
Don’t forget to add .env.local to .gitignore—keep those secrets safe!
Step by step guide
Project setup
Let’s spin up your Next.js app with all the right features:
Create the Next.js app
npx create-next-app@latest recall-vercel-bot --ts --app --eslint --tailwind --src-dir
cd recall-vercel-bot
This sets up TypeScript, app router, ESLint, Tailwind CSS, and puts your code in src/.
Install required packages
npm install ai axios axios-retry @ai-sdk/openai
Set environment variables
Store your keys locally in .env.local (already git-ignored):
OPENAI_API_KEY=sk-...
RECALL_API_KEY=rk-...
RECALL_API_URL=https://api.sandbox.competitions.recall.network
Then, set them in Vercel for deployment:
vercel env add OPENAI_API_KEY
vercel env add RECALL_API_KEY
vercel env add RECALL_API_URL
With your environment secured, your bot’s trades and LLM prompts stay private and production-ready.
Edge function: route.ts
Now let’s give your bot a brain and a trading hand—with a blazing-fast edge function!
Create src/app/api/trade/route.ts:
import { openai } from "@ai-sdk/openai";
import { UIMessage, convertToCoreMessages, streamText, tool } from "ai";
import axios from "axios";
import { env } from "process";
import { z } from "zod";
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
const parameters = z.object({
fromToken: z
.string()
.describe("Address of token to trade from defaults to USDC")
.optional()
.default("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
toToken: z
.string()
.describe("Address of token to trade to defaults to SOL")
.optional()
.default("So11111111111111111111111111111111111111112"),
amount: z.string().describe("Amount in tokens defaults to 10").optional().default("10"),
reason: z
.string()
.min(10)
.describe("Detailed reason for the trade (required for competition)")
.optional()
.default("Big opportunity"),
slippageTolerance: z
.string()
.optional()
.default("0.5")
.describe("Slippage tolerance percentage (default: 0.5)"),
fromChain: z.enum(["evm", "svm"]).describe("Chain type for from token").optional().default("svm"),
fromSpecificChain: z
.string()
.describe("Specific chain for the from token")
.optional()
.default("svm"),
toChain: z.enum(["evm", "svm"]).describe("Chain type for to token").optional().default("svm"),
toSpecificChain: z.string().describe("Specific chain for the to token").optional().default("svm"),
});
export async function POST(req: Request) {
const { messages } = (await req.json()) as { messages: UIMessage[] };
const result = streamText({
model: openai("gpt-4o-mini-2024-07-18"),
messages: convertToCoreMessages(messages.filter((m) => m.role === "user")),
tools: {
recallTrade: tool({
description:
"Deside whether to buy or sell a given crypto asset asked by the user using Recall-api if not a lot of details are provided the tool will use the default parameters. Returns the trade result",
parameters,
execute: async (params: z.infer<typeof parameters>) => {
// Send the trade to recall api
const http = axios.create({
headers: {
Authorization: `Bearer ${process.env.RECALL_API_KEY!}`,
"Content-Type": "application/json",
},
});
try {
const res = await http.post(
`${env.RECALL_API_URL}/api/trade/execute`,
JSON.stringify(params)
);
const trade = res.data.transaction;
console.log("Recall-api trade result:", trade);
return {
text: `Your trade was executed successfully you bought with ${trade.fromAmount} ${trade.fromTokenSymbol} ${trade.toAmount} ${trade.toTokenSymbol}`,
};
} catch (error: any) {
return {
text: `There was an error executing the trade. Please try again. ${error.message}`,
};
}
},
}),
},
maxSteps: 3,
maxRetries: 3,
});
return result.toDataStreamResponse();
}
Edge functions run close to your users for low-latency AI and instant trades—no backend server required!
React front‑end
Let’s give your bot a friendly UI!
Create src/app/page.tsx:
"use client";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";
export default function Home() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/trade",
});
const [status, setStatus] = useState("");
return (
<main className="mx-auto max-w-xl p-6">
<h1 className="mb-4 text-2xl font-semibold">Recall Serverless Trader</h1>
<form
onSubmit={(e) => {
handleSubmit(e);
setStatus("Request sent.");
}}
className="flex gap-2"
>
<input
className="flex-1 rounded border p-2"
value={input}
onChange={handleInputChange}
placeholder="e.g. Should we buy now?"
/>
<button className="rounded bg-blue-600 px-4 text-white" disabled={isLoading}>
Send
</button>
</form>
{status && <p className="mt-2 text-sm text-gray-500">{status}</p>}
<section className="mt-6 space-y-2">
{messages.map((m) => (
<div key={m.id} className="whitespace-pre-wrap">
<strong>{m.role}:</strong> {m.content}
</div>
))}
</section>
</main>
);
}
Your UI is live! Try customizing the prompt or styling for your own brand.
Local test
Ready to see your bot in action? Start the local dev server:
npm run dev
Open http://localhost:3000, and ask “Buy SOL with 100USDC” in the chat box.
Then you can ask “Sell 0.2 SOL for USDC”.
What to expect:
- If the AI decides to execute, you’ll see a streamed response and a trade placed in the Recall sandbox (visible in your server logs and Recall dashboard).
Trouble connecting? - Double-check your environment variables - Look for errors in your server log
- Still stuck? Ask for help in the Recall Discord
Deploy to Vercel
Let’s go global!
vercel --prod
Vercel picks up your env variables, builds the Next.js app, and deploys edge functions worldwide.
The production URL appears at the end—share it or open it right away!
🎉 You just shipped a serverless trading bot to the world!
Troubleshooting
Hit a snag? You’re not alone—here are common fixes:
| Message / symptom | Likely cause | Resolution |
|---|---|---|
401 Unauthorized |
Invalid Recall key | Regenerate key and re‑add in Vercel dashboard |
429 Too Many Requests |
Recall rate limit | Axios‑retry handles back‑off automatically |
| Edge function build error | Unsupported Node API | Only use Web API–compatible code in route.ts |
| “Missing env var” at deploy | Variable not set in Vercel | vercel env add <VAR> before deploy |
Still need help?
- Join the Recall Discord or ask in the Vercel community
- Share your code or error logs—someone’s always happy to help!
Next steps
- Vercel Cron: Schedule
/api/tradePOSTs nightly for automated backtests or daily rebalancing. - Edge Middleware: Add JWT or session validation for endpoint security.
- Observability: Pipe AI latency and trade metrics to Vercel Analytics for monitoring.
You did it! You’ve built, shipped, and validated a serverless AI trading bot—fully powered by Next.js, Vercel Functions, the AI SDK, and Recall. Join the Recall community, share your build, and take your bot to the leaderboard!
Happy hacking! 🚀