entries.batchCreate()
Efficiently create multiple entries in a single operation.
Signature
entries.batchCreate(
feedId: string,
entries: CreateEntryInput[],
options?: {
onProgress?: (completed: number, total: number) => void;
delayMs?: number;
}
): Promise<{ successful: Entry[]; failed: { input: CreateEntryInput; error: string }[] }>Parameters
feedId
- Type:
string - Required: Yes
- Description: The UUID of the feed to add entries to
entries
- Type:
CreateEntryInput[] - Required: Yes
- Description: Array of entry objects to create
interface CreateEntryInput {
content: string | Buffer | object; // Content data
mime_type?: string; // Auto-detected if not provided
title?: string; // Entry title
description?: string; // Entry description
metadata?: Record<string, any>; // Additional metadata
tags?: string[]; // Entry tags
is_free?: boolean; // Free access (default: true)
expires_at?: number; // Unix timestamp expiration
price?: { // Price for paid entries
amount: string;
currency: string;
};
}Returns
- Type:
Promise<{ successful: Entry[]; failed: { input: CreateEntryInput; error: string }[] }>
Returns an object containing:
successful: Array of successfully created entriesfailed: Array of failed entries with error details
Usage
Basic Example
const result = await grapevine.entries.batchCreate(feedId, [
{
title: 'Entry 1',
content: 'First entry content',
is_free: true
},
{
title: 'Entry 2',
content: 'Second entry content',
tags: ['update'],
description: 'An important update'
},
{
title: 'Entry 3',
content: { type: 'premium', data: 'exclusive content' },
tags: ['premium'],
is_free: false,
price: { amount: '1000000', currency: 'USDC' }
}
]);
console.log(`Created ${result.successful.length} entries`);
console.log(`Failed ${result.failed.length} entries`);
// Handle failures
if (result.failed.length > 0) {
console.error('Failed entries:', result.failed);
}Import from Data Source
// Import articles from external source with progress tracking
async function importArticles(feedId: string, articles: Article[]) {
const entries = articles.map(article => ({
title: article.title,
content: article.body,
description: article.summary,
tags: article.tags,
is_free: article.category === 'preview',
mime_type: 'text/markdown',
metadata: {
originalUrl: article.originalUrl,
author: article.author,
publishDate: article.publishDate
}
}));
const result = await grapevine.entries.batchCreate(feedId, entries, {
onProgress: (completed, total) => {
console.log(`Progress: ${completed}/${total} (${Math.round(completed/total*100)}%)`);
},
delayMs: 1000 // 1 second delay between requests to avoid rate limiting
});
console.log(`Successfully imported ${result.successful.length} articles`);
if (result.failed.length > 0) {
console.error(`Failed to import ${result.failed.length} articles:`);
result.failed.forEach(failure => {
console.error(`- ${failure.input.title}: ${failure.error}`);
});
}
return result;
}Mixed Content Types
const mixedEntries = await grapevine.entries.batchCreate(feedId, [
{
title: 'Market Data',
content: JSON.stringify({ btc: 50000, eth: 3000 }),
mime_type: 'application/json',
tags: ['data', 'market']
},
{
title: 'Analysis',
content: '## Market Analysis\n\nToday we see...',
mime_type: 'text/markdown',
tags: ['analysis']
},
{
title: 'Summary',
content: 'Quick market summary for today',
mime_type: 'text/plain',
is_free: true
}
]);Chunked Batch Processing
// Process large dataset in chunks
async function batchImport(feedId: string, allEntries: CreateEntryInput[]) {
const chunkSize = 10; // Process 10 at a time
const results: Entry[] = [];
for (let i = 0; i < allEntries.length; i += chunkSize) {
const chunk = allEntries.slice(i, i + chunkSize);
try {
const result = await grapevine.entries.batchCreate(feedId, chunk);
results.push(...result.successful);
console.log(`Processed ${i + chunk.length}/${allEntries.length}`);
if (result.failed.length > 0) {
console.log(`${result.failed.length} failed in this chunk`);
}
} catch (error) {
console.error(`Failed chunk ${i}-${i + chunkSize}:`, error);
}
}
return results;
}With Progress Tracking
async function batchCreateWithProgress(
feedId: string,
entries: CreateEntryInput[],
onProgress?: (current: number, total: number) => void
) {
const batchSize = 5;
const allCreated: Entry[] = [];
for (let i = 0; i < entries.length; i += batchSize) {
const batch = entries.slice(i, i + batchSize);
const result = await grapevine.entries.batchCreate(feedId, batch);
allCreated.push(...result.successful);
if (onProgress) {
onProgress(allCreated.length, entries.length);
}
}
return allCreated;
}
// Usage
const entries = await batchCreateWithProgress(
feedId,
largeDataset,
(current, total) => console.log(`Progress: ${current}/${total}`)
);Behind the Scenes
This method:
- Processes entries sequentially (not truly batched)
- Gets authentication headers for each entry
- Handles rate limiting with optional delays
- Handles 402 payments individually per entry
- Provides detailed success/failure tracking
- Stores content securely per entry
- Returns detailed results with error information
Error Handling
try {
const result = await grapevine.entries.batchCreate(feedId, entriesData);
console.log(`Successfully created ${result.successful.length} entries`);
if (result.failed.length > 0) {
console.log(`${result.failed.length} entries failed:`);
result.failed.forEach(({ input, error }) => {
console.log(`- "${input.title}": ${error}`);
});
}
} catch (error) {
if (error.message.includes('404')) {
console.error('Feed not found');
} else if (error.message.includes('403')) {
console.error('Not authorized - you can only add to your own feeds');
} else if (error.message.includes('402')) {
console.error('Payment failed for batch');
} else if (error.message.includes('413')) {
console.error('Batch too large - reduce number of entries');
} else {
console.error('Error:', error.message);
}
}Notes
- Authentication: Required - must be feed owner
- Payment: Individual x402 payments per entry (not batched)
- Rate Limiting: Use
delayMsoption to control request rate - Partial Success: Some entries can succeed while others fail
- Performance: Sequential processing with error handling
- MIME Types: Auto-detected if not specified
Related
- entries.create() - Create single entry
- entries.list() - List entries
- entries.paginate() - Paginate through entries