packages/docs/examples/rest-api.mdx
Create REST APIs for your elizaOS agents using your favorite web framework. These examples work without API keys using the classic ELIZA pattern-matching algorithm.
| Framework | Language | Directory | Port |
|---|---|---|---|
| Express | TypeScript | examples/rest-api/express/ | 3000 |
| Hono | TypeScript | examples/rest-api/hono/ | 3000 |
| Elysia | TypeScript | examples/rest-api/elysia/ | 3000 |
| FastAPI | Python | examples/rest-api/fastapi/ | 3000 |
| Flask | Python | examples/rest-api/flask/ | 3000 |
| Actix Web | Rust | examples/rest-api/actix/ | 3000 |
| Axum | Rust | examples/rest-api/axum/ | 3000 |
| Rocket | Rust | examples/rest-api/rocket/ | 3000 |
All examples expose the same REST API:
Returns information about the agent.
curl http://localhost:3000/
Response:
{
"name": "Eliza",
"bio": "A Rogerian psychotherapist simulation",
"status": "ready"
}
Health check endpoint.
curl http://localhost:3000/health
Response:
{
"status": "healthy",
"runtime": "elizaos",
"uptime": 12345
}
Send a message to the agent.
curl -X POST http://localhost:3000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello, how are you?"}'
Response:
{
"response": "How do you do. Please state your problem.",
"character": "Eliza",
"userId": "550e8400-e29b-41d4-a716-446655440000"
}
const app = express(); app.use(express.json());
// Character definition const character = { name: 'Eliza', bio: 'A Rogerian psychotherapist simulation created at MIT in 1966.', };
// Initialize runtime const runtime = new AgentRuntime({ character, plugins: [localdbPlugin, elizaClassicPlugin], });
await runtime.initialize();
// Routes app.get('/', (req, res) => { res.json({ name: character.name, bio: character.bio, status: 'ready', }); });
app.get('/health', (req, res) => { res.json({ status: 'healthy', runtime: 'elizaos-typescript', uptime: process.uptime(), }); });
app.post('/chat', async (req, res) => { const { message, userId = uuidv4() } = req.body;
if (!message) { return res.status(400).json({ error: 'Message is required' }); }
try { const response = await runtime.useModel(ModelType.TEXT_LARGE, { prompt: message, });
res.json({
response: String(response),
character: character.name,
userId,
});
} catch (error) { res.status(500).json({ error: 'Failed to process message' }); } });
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(Eliza API running on http://localhost:${PORT});
});
</Tab>
<Tab title="FastAPI (Python)">
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from elizaos import AgentRuntime, Character
from elizaos.plugin_eliza_classic import eliza_classic_plugin
import uuid
import time
app = FastAPI()
# Character definition
character = Character(
name="Eliza",
bio="A Rogerian psychotherapist simulation created at MIT in 1966.",
)
# Initialize runtime
runtime = AgentRuntime(
character=character,
plugins=[eliza_classic_plugin],
)
start_time = time.time()
class ChatRequest(BaseModel):
message: str
userId: str | None = None
class ChatResponse(BaseModel):
response: str
character: str
userId: str
@app.on_event("startup")
async def startup():
await runtime.initialize()
@app.get("/")
async def root():
return {
"name": character.name,
"bio": character.bio,
"status": "ready",
}
@app.get("/health")
async def health():
return {
"status": "healthy",
"runtime": "elizaos-python",
"uptime": time.time() - start_time,
}
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
if not request.message:
raise HTTPException(status_code=400, detail="Message is required")
user_id = request.userId or str(uuid.uuid4())
try:
from elizaos import ChannelType, Content, Memory, string_to_uuid
message = Memory(
entity_id=string_to_uuid(user_id),
room_id=string_to_uuid("rest-api-room"),
content=Content(
text=request.message,
source="rest-api",
channel_type=ChannelType.DM.value,
),
)
result = await runtime.message_service.handle_message(runtime, message)
response = (
result.response_content.text
if result.response_content and result.response_content.text
else ""
)
return ChatResponse(
response=str(response),
character=character.name,
userId=user_id,
)
except Exception as e:
raise HTTPException(status_code=500, detail="Failed to process message")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3000)
#[derive(Deserialize)] struct ChatRequest { message: String, #[serde(rename = "userId")] user_id: Option<String>, }
#[derive(Serialize)] struct ChatResponse { response: String, character: String, #[serde(rename = "userId")] user_id: String, }
#[derive(Serialize)] struct InfoResponse { name: String, bio: String, status: String, }
#[derive(Serialize)] struct HealthResponse { status: String, runtime: String, uptime: f64, }
struct AppState { runtime: Arc<AgentRuntime>, character_name: String, character_bio: String, start_time: Instant, }
async fn root(data: web::Data<AppState>) -> Result<HttpResponse> { Ok(HttpResponse::Ok().json(InfoResponse { name: data.character_name.clone(), bio: data.character_bio.clone(), status: "ready".to_string(), })) }
async fn health(data: web::Data<AppState>) -> Result<HttpResponse> { Ok(HttpResponse::Ok().json(HealthResponse { status: "healthy".to_string(), runtime: "elizaos-rust".to_string(), uptime: data.start_time.elapsed().as_secs_f64(), })) }
async fn chat( data: web::Data<AppState>, req: web::Json<ChatRequest>, ) -> Result<HttpResponse> { let user_id = req.user_id.clone() .unwrap_or_else(|| Uuid::new_v4().to_string());
let content = elizaos::types::Content {
text: Some(req.message.clone()),
source: Some("rest-api".to_string()),
channel_type: Some(elizaos::types::ChannelType::Dm),
..Default::default()
};
let mut msg =
elizaos::types::Memory::new(elizaos::types::UUID::new_v4(), elizaos::types::UUID::new_v4(), content);
let result = data.runtime
.message_service()
.handle_message(data.runtime.as_ref(), &mut msg, None, None)
.await
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to process"))?;
let response = result
.response_content
.and_then(|c| c.text)
.unwrap_or_default();
Ok(HttpResponse::Ok().json(ChatResponse {
response,
character: data.character_name.clone(),
user_id,
}))
}
#[actix_web::main] async fn main() -> std::io::Result<()> { let character = parse_character(r#"{ "name": "Eliza", "bio": "A Rogerian psychotherapist simulation created at MIT in 1966." }"#).unwrap();
let runtime = AgentRuntime::new(RuntimeOptions {
character: Some(character.clone()),
plugins: vec![create_eliza_classic_plugin()],
..Default::default()
}).await.unwrap();
runtime.initialize().await.unwrap();
let state = web::Data::new(AppState {
runtime: Arc::new(runtime),
character_name: character.name.clone(),
character_bio: character.bio.clone(),
start_time: Instant::now(),
});
println!("Eliza API running on http://localhost:3000");
HttpServer::new(move || {
App::new()
.app_data(state.clone())
.route("/", web::get().to(root))
.route("/health", web::get().to(health))
.route("/chat", web::post().to(chat))
})
.bind("0.0.0.0:3000")?
.run()
.await
}
</Tab>
</Tabs>
---
## Framework Comparison
| Feature | Express | Hono | Elysia | FastAPI | Flask | Actix | Axum | Rocket |
|---------|---------|------|--------|---------|-------|-------|------|--------|
| Async | ✅ | ✅ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ |
| Type Safety | ⚠️ | ✅ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ |
| Performance | Good | Great | Great | Good | Fair | Excellent | Excellent | Great |
| Bundle Size | Medium | Small | Small | N/A | N/A | Small | Small | Medium |
---
## Testing the API
```bash
# Get agent info
curl http://localhost:3000/
# Health check
curl http://localhost:3000/health
# Send a message
curl -X POST http://localhost:3000/chat \
-H "Content-Type: application/json" \
-d '{"message": "I am feeling sad today"}'
# Expected response:
# {"response": "I am sorry to hear that you are feeling that way.", ...}
All examples support the PORT environment variable:
PORT=8080 bun run start # TypeScript
PORT=8080 python server.py # Python
PORT=8080 cargo run --release # Rust
To use OpenAI instead of pattern matching, swap the plugin:
<Tabs> <Tab title="TypeScript"> ```typescript import { openaiPlugin } from '@elizaos/plugin-openai'; import { plugin as sqlPlugin } from '@elizaos/plugin-sql';const runtime = new AgentRuntime({ character, plugins: [sqlPlugin, openaiPlugin], // Replace elizaClassicPlugin });
</Tab>
<Tab title="Python">
```python
from elizaos_plugin_openai import get_openai_plugin
runtime = AgentRuntime(
character=character,
plugins=[get_openai_plugin()],
)
let runtime = AgentRuntime::new(RuntimeOptions { character: Some(character), plugins: vec![create_openai_plugin()?], ..Default::default() }).await?;
</Tab>
</Tabs>
<Warning>
OpenAI requires `OPENAI_API_KEY` environment variable.
</Warning>
---
## Next Steps
<CardGroup cols={2}>
<Card title="Browser Examples" icon="browser" href="/examples/browser">
Run agents client-side without a server
</Card>
<Card title="Serverless Examples" icon="cloud" href="/examples/serverless">
Deploy to AWS, GCP, Vercel, or Cloudflare
</Card>
</CardGroup>