leaderboards.recentEntries()
Get the most recently published entries across all feeds on the platform.
Signature
leaderboards.recentEntries(query?: {
page_size?: number;
page_token?: string;
}): Promise<PaginatedResponse<RecentEntry>>Parameters
query (optional)
{
page_size?: number; // Results per page (default: 20)
page_token?: string; // Pagination token for next page
}Returns
- Type:
Promise<PaginatedResponse<RecentEntry>>
interface PaginatedResponse<RecentEntry> {
data: RecentEntry[];
next_page_token?: string;
has_more: boolean;
}
interface RecentEntry {
id: string;
feed_id: string;
cid: string;
mime_type: string;
title?: string | null;
description?: string | null;
metadata?: string | null;
tags?: string[] | null;
price: string;
asset: string;
is_free: boolean;
expires_at?: number | null;
piid?: string | null;
feed_name: string;
feed_owner_id: string;
owner_wallet: string;
category_name: string;
created_at: number;
updated_at: number;
}Usage
Basic Example
const recent = await grapevine.leaderboards.recentEntries();
recent.data.forEach(entry => {
console.log(`${entry.title || 'Untitled'} in "${entry.feed_name}"`);
console.log(` Category: ${entry.category_name}`);
console.log(` Price: ${entry.is_free ? 'Free' : formatUSDC(entry.price)}`);
console.log(` Posted: ${new Date(entry.created_at * 1000).toLocaleString()}`);
});
function formatUSDC(weiAmount: string): string {
const usdc = Number(BigInt(weiAmount)) / 1e6;
return `${usdc.toFixed(2)}`;
}Content Feed
// Get latest 50 entries
const latest = await grapevine.leaderboards.recentEntries({ page_size: 50 });
console.log('Latest content:');
latest.data.forEach(entry => {
const time = new Date(entry.created_at * 1000);
const timeAgo = getTimeAgo(time);
console.log(`[${timeAgo}] ${entry.title || 'Untitled'}`);
console.log(` ${entry.feed_name} • ${entry.category_name}`);
});
function getTimeAgo(date: Date): string {
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
if (seconds < 60) return `${seconds}s ago`;
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
return `${Math.floor(seconds / 86400)}d ago`;
}Paginate Through All Recent
async function getAllRecentEntries(limit: number = 100) {
const entries: RecentEntry[] = [];
let pageToken: string | undefined;
while (entries.length < limit) {
const result = await grapevine.leaderboards.recentEntries({
page_size: Math.min(100, limit - entries.length),
page_token: pageToken
});
entries.push(...result.data);
if (!result.has_more) break;
pageToken = result.next_page_token;
}
return entries;
}Filter by Category
async function getRecentByCategory(categoryName: string) {
const recent = await grapevine.leaderboards.recentEntries({ page_size: 100 });
return recent.data.filter(
entry => entry.category_name.toLowerCase() === categoryName.toLowerCase()
);
}
// Usage
const techEntries = await getRecentByCategory('Technology');
console.log(`Found ${techEntries.length} recent tech entries`);Free Content Only
async function getRecentFreeContent() {
const recent = await grapevine.leaderboards.recentEntries({ page_size: 50 });
return recent.data.filter(entry => entry.is_free);
}Recent Entries Component
function RecentEntriesFeed() {
const [entries, setEntries] = useState<RecentEntry[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
grapevine.leaderboards.recentEntries({ page_size: 20 })
.then(result => {
setEntries(result.data);
setLoading(false);
});
}, []);
if (loading) return <div>Loading recent content...</div>;
return (
<div className="entries-feed">
<h2>Recent Content</h2>
{entries.map(entry => (
<article key={entry.id} className="entry-card">
<header>
<h3>{entry.title || 'Untitled'}</h3>
<span className="time">{getTimeAgo(entry.created_at)}</span>
</header>
<p className="meta">
<span className="feed">{entry.feed_name}</span>
<span className="category">{entry.category_name}</span>
</p>
{entry.description && (
<p className="description">{entry.description}</p>
)}
<footer>
<span className="price">
{entry.is_free ? '🆓 Free' : `💰 ${formatUSDC(entry.price)}`}
</span>
<span className="type">{entry.mime_type}</span>
</footer>
{entry.tags && entry.tags.length > 0 && (
<div className="tags">
{entry.tags.map(tag => (
<span key={tag} className="tag">#{tag}</span>
))}
</div>
)}
</article>
))}
</div>
);
}
function getTimeAgo(timestamp: number): string {
const seconds = Math.floor(Date.now() / 1000 - timestamp);
if (seconds < 60) return 'Just now';
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
return `${Math.floor(seconds / 86400)}d ago`;
}Content Analytics
async function analyzeRecentContent() {
const recent = await grapevine.leaderboards.recentEntries({ page_size: 100 });
const entries = recent.data;
// Count by category
const byCategory = entries.reduce((acc, e) => {
acc[e.category_name] = (acc[e.category_name] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Count free vs paid
const freeCount = entries.filter(e => e.is_free).length;
const paidCount = entries.length - freeCount;
// Count by MIME type
const byMimeType = entries.reduce((acc, e) => {
const type = e.mime_type.split('/')[0]; // e.g., "text", "image"
acc[type] = (acc[type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return {
total: entries.length,
byCategory,
freeCount,
paidCount,
freePercentage: `${((freeCount / entries.length) * 100).toFixed(1)}%`,
byMimeType
};
}Notes
- Authentication: Not required - public endpoint
- Ordering: Results are ordered by creation time, newest first
- Pagination: Supports cursor-based pagination for large result sets
- Includes: Feed and category info for easy display
Related
- entries.list() - Entries for a specific feed
- leaderboards.trending() - Trending feeds
- feeds.list() - Browse all feeds