@embed-ai/sdk
The @embed-ai/sdk is a TypeScript/JavaScript SDK that makes it easy to integrate real-time, personalized Web3 feeds into your application. It provides a simple interface to the embed recommendation APIs with built-in error handling, automatic retries, and type safety.
Table of Contents
- Installation
- Quick Start
- Client Initialization
- API Reference
- Response Types
- Error Handling
- Configuration
- Examples
- Best Practices
Installation
Install the SDK using your preferred package manager:
# Using bun (recommended)
bun install @embed-ai/sdk
# Using npm
npm install @embed-ai/sdk
# Using yarn
yarn add @embed-ai/sdk
# Using pnpm
pnpm add @embed-ai/sdkRequirements
- Node.js 18+ or Bun
- TypeScript 4.5+ (optional, but recommended for type safety)
Quick Start
Get your first feed in three steps:
import { getClient } from "@embed-ai/sdk";
// 1. Initialize the client with your API key
const client = getClient(process.env.API_KEY_EMBED!);
// 2. Fetch a feed for a user
const feed = await client.feed.byUserId("16085", "feed_390", {
top_k: 10,
});
// 3. Use the feed data
console.log(`Found ${feed.length} recommendations`);
feed.forEach((item) => {
console.log(`Item: ${item.item_id}, Score: ${item.score}`);
});Client Initialization
Basic Initialization
import { getClient } from "@embed-ai/sdk";
const client = getClient("mbd-your-api-key-here");Using Environment Variables
import { getClient } from "@embed-ai/sdk";
const client = getClient(process.env.API_KEY_EMBED!);Advanced Configuration
The SDK client includes built-in features:
- Automatic Retries: Failed requests are automatically retried
- Error Handling: Standardized error responses
- Type Safety: Full TypeScript support
- Request Formatting: Automatic header and body formatting
API Reference
Feed Methods
client.feed.byUserId(fid, feedId, options?)
client.feed.byUserId(fid, feedId, options?)Fetches personalized feed recommendations for a Farcaster user ID (FID).
Parameters:
fid(string, required): Farcaster ID of the userfeedId(string, required): Feed configuration ID (e.g.,"feed_390")options(object, optional): Request optionstop_k(number, optional): Number of recommendations to return (1-500, default: 25)return_metadata(boolean, optional): Include full post metadata (default: true)impression_count(number, optional): Number of posts considered as seen by the user
Returns: Promise<FeedItem[]>
Example:
const feed = await client.feed.byUserId("16085", "feed_390", {
top_k: 25,
return_metadata: true,
impression_count: 1,
});client.feed.byWalletAddress(walletAddress, feedId, options?)
client.feed.byWalletAddress(walletAddress, feedId, options?)Fetches personalized feed recommendations for a wallet address.
Parameters:
walletAddress(string, required): Ethereum wallet address (must start with0x)feedId(string, required): Feed configuration IDoptions(object, optional): Same asbyUserId
Returns: Promise<FeedItem[]>
Example:
const feed = await client.feed.byWalletAddress(
"0x1234567890123456789012345678901234567890",
"feed_390",
{ top_k: 10 }
);Response Types
FeedItem
Each item in the feed response contains the following structure:
interface FeedItem {
// Identifiers
item_id: string; // Post hash/ID (Ethereum address format)
// Scoring Metrics
popular_score: number; // Popularity score (0-1)
trending_score: number; // Trending signal score (0-1)
affinity_score: number; // User affinity score based on following graph
score: number; // Final combined recommendation score
adjusted_score?: number; // Score adjusted by time decay and other factors
// Engagement Metrics
text_length: number; // Length of post text in characters
following_engagement?: {
// Engagement from users the viewer follows
comment: number;
share: number;
like: number;
};
// Social Proof
social_proof?: Array<{
// Users in viewer's network who interacted
user_id: number; // FID of the user
timestamp: number; // Unix timestamp of interaction
interaction: "like" | "share" | "comment";
}>;
// Source Information
source_feed: string; // Feed source identifier (e.g., "main:feed_3380")
// Full Post Metadata (only included if return_metadata: true)
metadata?: {
// Content
text: string; // Post content
embed_items: string[]; // Array of embedded media URLs
// Timestamps
timestamp: number; // Unix timestamp when post was created
// Threading
root_parent_hash: string;
parent_hash: string | null;
root_parent_url: string | null;
mentioned_profiles: string[];
// AI Analysis
ai_labels: {
topics: string[]; // Topic categories
sentiment: string[]; // Sentiment analysis
emotion: string[]; // Emotional tone
moderation: string[]; // Moderation flags
web3_topics: string[]; // Web3-specific topics
};
// App Information
app_fid: string; // FID of the app that created the post
// Location
geo_location: string | null; // Geographic location (lat,long)
// Engagement Counts
likes_count: number;
comments_count: number;
shares_count: number;
// Author Information
author: {
user_id: number; // FID
username: string;
display_name: string;
pfp_url: string; // Profile picture URL
spam_score?: number; // Spam detection score
};
};
}Example Response
[
{
item_id: "0x295c84a2e7973d8dd6cd6e946937da39c5138175",
popular_score: 13.251785067452587,
trending_score: 0.32665482296015197,
affinity_score: 0.06666666666666667,
following_engagement: {
comment: 0,
share: 0,
like: 2,
},
social_proof: [
{
user_id: 8109,
timestamp: 1752655366,
interaction: "like",
},
{
user_id: 646397,
timestamp: 1752653278,
interaction: "like",
},
],
score: 0.06666666666666667,
source_feed: "warmup:main:feed_392",
metadata: {
text: "Eventually crypto will just be a part of everyday life...",
embed_items: [],
timestamp: 1752651678,
root_parent_hash: "0x295c84a2e7973d8dd6cd6e946937da39c5138175",
parent_hash: null,
root_parent_url: null,
mentioned_profiles: [],
ai_labels: {
topics: [],
sentiment: [],
emotion: [],
moderation: [],
web3_topics: [],
},
app_fid: "9152",
geo_location: "45.81,9.09",
likes_count: 21,
comments_count: 11,
shares_count: 2,
author: {
user_id: 2802,
username: "garrett",
display_name: "Garrett",
pfp_url: "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/...",
},
},
},
// ... more items
];Error Handling
The SDK automatically handles errors and retries failed requests. However, you should still handle errors in your application:
import { getClient } from "@embed-ai/sdk";
const client = getClient(process.env.API_KEY_EMBED!);
try {
const feed = await client.feed.byUserId("16085", "feed_390", { top_k: 10 });
// Use feed data
} catch (error) {
if (error.status === 401) {
console.error("Invalid API key");
} else if (error.status === 400) {
console.error("Invalid request:", error.message);
} else if (error.status === 500) {
console.error("Server error, please try again later");
} else {
console.error("Unexpected error:", error);
}
}Error Types
- 401 Unauthorized: Invalid or missing API key
- 400 Bad Request: Invalid parameters (e.g., invalid feed_id, user_id format)
- 500 Internal Server Error: Server-side error
Configuration
Environment Variables
Store your API key securely:
# .env file
API_KEY_EMBED=mbd-your-api-key-here// Load from environment
const client = getClient(process.env.API_KEY_EMBED!);Retry Configuration
The SDK includes automatic retry logic for transient failures. Retry behavior is configured internally but may be customizable in advanced configurations.
Examples
Basic Feed Fetch
import { getClient } from "@embed-ai/sdk";
const client = getClient(process.env.API_KEY_EMBED!);
// Get feed for a user
const feed = await client.feed.byUserId("16085", "feed_390", {
top_k: 25,
return_metadata: true,
});
// Render feed items
feed.forEach((item, index) => {
console.log(`${index + 1}. ${item.metadata?.text}`);
console.log(` Author: ${item.metadata?.author.display_name}`);
console.log(` Score: ${item.score}`);
console.log(` Likes: ${item.metadata?.likes_count}`);
});Wallet-Based Feed
import { getClient } from "@embed-ai/sdk";
const client = getClient(process.env.API_KEY_EMBED!);
const walletAddress = "0x1234567890123456789012345678901234567890";
const feed = await client.feed.byWalletAddress(walletAddress, "feed_390", {
top_k: 10,
return_metadata: true,
});Server-Side Rendering (Next.js API Route)
// app/api/feed/route.ts
import { getClient } from "@embed-ai/sdk";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const fid = searchParams.get("fid");
const feedId = searchParams.get("feedId") || "feed_390";
const topK = parseInt(searchParams.get("top_k") || "25");
if (!fid) {
return NextResponse.json(
{ error: "fid parameter is required" },
{ status: 400 }
);
}
try {
const client = getClient(process.env.API_KEY_EMBED!);
const feed = await client.feed.byUserId(fid, feedId, {
top_k: topK,
return_metadata: true,
});
return NextResponse.json({ feed });
} catch (error) {
return NextResponse.json(
{ error: "Failed to fetch feed" },
{ status: 500 }
);
}
}React Component Example
// components/Feed.tsx
"use client";
import { useEffect, useState } from "react";
interface FeedItem {
item_id: string;
score: number;
metadata?: {
text: string;
author: {
display_name: string;
pfp_url: string;
};
likes_count: number;
};
}
export function Feed({ fid }: { fid: string }) {
const [feed, setFeed] = useState<FeedItem[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchFeed() {
try {
// Call your API route that uses the SDK
const response = await fetch(`/api/feed?fid=${fid}`);
const data = await response.json();
setFeed(data.feed);
} catch (error) {
console.error("Failed to fetch feed:", error);
} finally {
setLoading(false);
}
}
fetchFeed();
}, [fid]);
if (loading) return <div>Loading feed...</div>;
return (
<div>
{feed.map((item) => (
<div key={item.item_id} className="feed-item">
<div className="author">
<img src={item.metadata?.author.pfp_url} alt="Profile" />
<span>{item.metadata?.author.display_name}</span>
</div>
<p>{item.metadata?.text}</p>
<div className="engagement">
<span>❤️ {item.metadata?.likes_count}</span>
<span>Score: {item.score.toFixed(3)}</span>
</div>
</div>
))}
</div>
);
}Filtering and Processing Feed Items
import { getClient } from "@embed-ai/sdk";
const client = getClient(process.env.API_KEY_EMBED!);
const feed = await client.feed.byUserId("16085", "feed_390", {
top_k: 50,
return_metadata: true,
});
// Filter high-scoring items
const highQualityItems = feed.filter((item) => item.score > 0.5);
// Filter by engagement
const popularItems = feed.filter(
(item) => (item.metadata?.likes_count || 0) > 10
);
// Group by author
const byAuthor = feed.reduce((acc, item) => {
const authorId = item.metadata?.author.user_id;
if (authorId) {
if (!acc[authorId]) acc[authorId] = [];
acc[authorId].push(item);
}
return acc;
}, {} as Record<number, typeof feed>);Available Feed Templates
The SDK works with pre-configured feed templates. Use these feedId values:
| Feed ID | Name | Description |
|---|---|---|
feed_390 | For You | Personalized feed based on user behavior |
feed_624 | Zora Feed | Zora coin/posts feed |
feed_625 | Short Form Video | Video-focused content feed |
feed_626 | Miniapps | Mini-app related content |
feed_627 | New York | Location-based feed for New York |
feed_628 | Coinbase Wallet Creators | Content from Coinbase Wallet creators |
feed_629 | Warpcast Creators | Content from Warpcast creators |
You can also use custom feed IDs created through the Feed Management API.
Best Practices
1. Server-Side Usage
Always use the SDK on the server side to keep your API keys secure:
// ✅ Good: Server-side (API route, server component)
// app/api/feed/route.ts
const client = getClient(process.env.API_KEY_EMBED!);
const feed = await client.feed.byUserId(fid, "feed_390");
// ❌ Bad: Client-side (exposes API key)
// components/Feed.tsx
const client = getClient("mbd-your-key"); // Don't do this!2. Error Handling
Always wrap SDK calls in try-catch blocks:
try {
const feed = await client.feed.byUserId(fid, feedId);
// Process feed
} catch (error) {
// Handle error appropriately
console.error("Feed fetch failed:", error);
// Return fallback or show error message
}3. Caching
Consider caching feed responses to reduce API calls:
// Simple in-memory cache
const feedCache = new Map<string, { data: FeedItem[]; timestamp: number }>();
const CACHE_TTL = 60000; // 1 minute
async function getCachedFeed(fid: string, feedId: string) {
const cacheKey = `${fid}-${feedId}`;
const cached = feedCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const feed = await client.feed.byUserId(fid, feedId);
feedCache.set(cacheKey, { data: feed, timestamp: Date.now() });
return feed;
}4. Type Safety
Use TypeScript for better type safety:
import { getClient } from "@embed-ai/sdk";
// TypeScript will provide autocomplete and type checking
const client = getClient(process.env.API_KEY_EMBED!);
const feed = await client.feed.byUserId("16085", "feed_390");
// feed is typed as FeedItem[]5. Environment Variables
Never hardcode API keys:
// ✅ Good
const client = getClient(process.env.API_KEY_EMBED!);
// ❌ Bad
const client = getClient(
"mbd-a4790a01cf57c80b08afc644e717c28c4e49f629bc399b55d75fb63a6bee885f"
);6. Pagination
For large feeds, consider fetching in batches:
async function getPaginatedFeed(fid: string, feedId: string, pageSize = 25) {
const allItems: FeedItem[] = [];
let offset = 0;
while (true) {
const feed = await client.feed.byUserId(fid, feedId, {
top_k: pageSize,
// Note: The API may not support offset directly,
// this is a conceptual example
});
if (feed.length === 0) break;
allItems.push(...feed);
offset += pageSize;
// Add delay to avoid rate limiting
await new Promise((resolve) => setTimeout(resolve, 100));
}
return allItems;
}Troubleshooting
Common Issues
1. "Invalid API key" error
- Verify your API key at console.mbd.xyz
- Ensure the key starts with
mbd- - Check that the key hasn't been revoked
2. "Invalid feed_id" error
- Verify the feed ID exists and is accessible with your API key
- Use one of the template feed IDs listed above
- Check feed permissions in the console
3. Empty feed responses
- The user may not have enough interaction history
- Try a different feed template
- Check that the user ID or wallet address is valid
4. Type errors in TypeScript
- Ensure you're using TypeScript 4.5+
- Check that
@embed-ai/sdktypes are properly installed - Restart your TypeScript server
Additional Resources
- Getting Started Guide
- Authentication Documentation
- Feed Management API Documentation
- Feed Deployment API Documentation
- Console Dashboard
Support
For SDK issues or questions:
- Check the documentation
- Contact support: [email protected]
- Review API documentation for endpoint details
Updated 2 days ago
