The History API provides comprehensive management of your audio generation history, allowing you to retrieve, download, and manage previously generated audio files with detailed metadata and search capabilities.
import {
ElevenLabsClient,
type HistoryGetAllRequest,
type GetSpeechHistoryResponse,
type HistoryItemResponse,
type DeleteHistoryItemResponse,
type HistoryDownloadRequest,
type HistoryGetAllRequestSource
} from 'elevenlabs';const client = new ElevenLabsClient();
// Get recent audio generation history
const historyResponse = await client.history.getAll();
console.log(`Total history items: ${historyResponse.has_more ? 'More than ' : ''}${historyResponse.history.length}`);
historyResponse.history.forEach((item, index) => {
console.log(`${index + 1}. ${item.text?.substring(0, 50)}...`);
console.log(` Voice: ${item.voice_name} (${item.voice_id})`);
console.log(` Created: ${new Date(item.date_unix * 1000).toLocaleString()}`);
console.log(` Character count: ${item.character_count_change_from}, ${item.character_count_change_to}`);
});// Get history with filters and pagination
const filteredHistory = await client.history.getAll({
page_size: 50,
voice_id: "21m00Tcm4TlvDq8ikWAM", // Filter by specific voice
search: "important announcement", // Search in text content
source: "tts" // Filter by generation source
});
console.log('Filtered results:', filteredHistory.history.length);// Paginate through large history collections
let allHistoryItems = [];
let startAfter: string | undefined;
let hasMore = true;
while (hasMore) {
const batch = await client.history.getAll({
page_size: 100,
start_after_history_item_id: startAfter
});
allHistoryItems.push(...batch.history);
if (batch.history.length > 0) {
startAfter = batch.history[batch.history.length - 1].history_item_id;
hasMore = batch.has_more;
} else {
hasMore = false;
}
console.log(`Loaded ${allHistoryItems.length} history items...`);
}
console.log(`Total history items: ${allHistoryItems.length}`);interface HistoryGetAllRequest {
/**
* Maximum items to return (max 1000, default 100)
*/
page_size?: number;
/**
* Pagination cursor - ID to start fetching after
* Used for paginating through large collections
*/
start_after_history_item_id?: string;
/**
* Filter by specific voice ID
* Use voices.getAll() to list available voice IDs
*/
voice_id?: string;
/**
* Search term for filtering history items
* Searches in text content and metadata
* Requires 'source' parameter when provided
*/
search?: string;
/**
* Source of the generated history item
* Used to filter by generation method
*/
source?: HistoryGetAllRequestSource;
}type HistoryGetAllRequestSource =
| "tts" // Text-to-speech generations
| "sts" // Speech-to-speech conversions
| "dubbing" // Dubbing projects
| "sound_effects" // Sound effects generation
| "voice_clone" // Voice cloning operations
| "instant_voice_cloning"; // Instant voice cloning// Get detailed information for a specific history item
const historyItem = await client.history.get("history_item_id_here");
console.log('History item details:', {
id: historyItem.history_item_id,
text: historyItem.text,
voice: {
id: historyItem.voice_id,
name: historyItem.voice_name,
category: historyItem.voice_category
},
generation: {
date: new Date(historyItem.date_unix * 1000),
characterCount: historyItem.character_count_change_from,
model: historyItem.model_id,
settings: historyItem.settings
},
metadata: {
source: historyItem.source,
state: historyItem.state,
feedbackThumbsUp: historyItem.feedback?.thumbs_up,
feedbackIssues: historyItem.feedback?.feedback,
shareLink: historyItem.share_link_id
}
});interface HistoryItemResponse {
/** Unique identifier for the history item */
history_item_id: string;
/** Generated text content */
text?: string;
/** Voice information */
voice_id: string;
voice_name?: string;
voice_category?: string;
/** Generation metadata */
date_unix: number;
character_count_change_from: number;
character_count_change_to: number;
content_type?: string;
state?: string;
/** Generation settings */
model_id?: string;
settings?: VoiceSettings;
/** Source and classification */
source?: HistoryGetAllRequestSource;
/** User feedback */
feedback?: {
thumbs_up?: boolean;
feedback?: string;
emotions?: boolean;
inaccurate_clone?: boolean;
glitches?: boolean;
audio_quality?: boolean;
other?: boolean;
};
/** Sharing information */
share_link_id?: string;
}// Download audio for a specific history item
const audioStream = await client.history.getAudio("history_item_id_here");
// Play the audio
import { play } from 'elevenlabs';
await play(audioStream);import * as fs from 'fs';
import { pipeline } from 'stream';
import { promisify } from 'util';
const pipelineAsync = promisify(pipeline);
// Download and save history audio
const audioStream = await client.history.getAudio("history_item_id_here");
const outputPath = 'downloaded_audio.mp3';
await pipelineAsync(
audioStream,
fs.createWriteStream(outputPath)
);
console.log(`Audio saved to ${outputPath}`);// Download multiple history items as an archive
const archiveStream = await client.history.downloadHistory({
history_item_ids: [
"history_item_1",
"history_item_2",
"history_item_3"
]
});
// Save the archive
await pipelineAsync(
archiveStream,
fs.createWriteStream('history_archive.zip')
);// Delete a specific history item
const deleteResult = await client.history.delete("history_item_id_here");
console.log('Deletion successful:', deleteResult.success);// Delete multiple items based on criteria
const oldHistory = await client.history.getAll({
page_size: 1000
});
const thirtyDaysAgo = Date.now() / 1000 - (30 * 24 * 60 * 60);
const itemsToDelete = oldHistory.history.filter(
item => item.date_unix < thirtyDaysAgo
);
console.log(`Deleting ${itemsToDelete.length} old history items...`);
for (const item of itemsToDelete) {
try {
await client.history.delete(item.history_item_id);
console.log(`Deleted: ${item.text?.substring(0, 30)}...`);
} catch (error) {
console.error(`Failed to delete ${item.history_item_id}:`, error);
}
}// Search for specific content
const searchResults = await client.history.getAll({
search: "customer announcement",
source: "tts",
page_size: 100
});
// Filter by voice and date range (client-side filtering)
const voiceHistory = await client.history.getAll({
voice_id: "pNInz6obpgDQGcFmaJgB",
page_size: 1000
});
const lastWeek = Date.now() / 1000 - (7 * 24 * 60 * 60);
const recentVoiceHistory = voiceHistory.history.filter(
item => item.date_unix > lastWeek
);
console.log(`Recent items for this voice: ${recentVoiceHistory.length}`);// Analyze history usage patterns
const fullHistory = await client.history.getAll({
page_size: 1000
});
const analytics = {
totalItems: fullHistory.history.length,
totalCharacters: fullHistory.history.reduce(
(sum, item) => sum + (item.character_count_change_to - item.character_count_change_from),
0
),
voiceUsage: new Map<string, number>(),
modelUsage: new Map<string, number>(),
sourceBreakdown: new Map<string, number>()
};
fullHistory.history.forEach(item => {
// Voice usage
const voiceKey = `${item.voice_name} (${item.voice_id})`;
analytics.voiceUsage.set(voiceKey, (analytics.voiceUsage.get(voiceKey) || 0) + 1);
// Model usage
if (item.model_id) {
analytics.modelUsage.set(item.model_id, (analytics.modelUsage.get(item.model_id) || 0) + 1);
}
// Source breakdown
if (item.source) {
analytics.sourceBreakdown.set(item.source, (analytics.sourceBreakdown.get(item.source) || 0) + 1);
}
});
console.log('History Analytics:', analytics);// Export history metadata to JSON
const historyExport = await client.history.getAll({
page_size: 1000
});
const exportData = {
exportDate: new Date().toISOString(),
totalItems: historyExport.history.length,
items: historyExport.history.map(item => ({
id: item.history_item_id,
text: item.text,
voiceId: item.voice_id,
voiceName: item.voice_name,
date: new Date(item.date_unix * 1000).toISOString(),
characterCount: item.character_count_change_to - item.character_count_change_from,
model: item.model_id,
source: item.source,
settings: item.settings
}))
};
fs.writeFileSync('history_export.json', JSON.stringify(exportData, null, 2));
console.log('History exported to history_export.json');// Comprehensive backup of audio and metadata
async function backupHistory(client: ElevenLabsClient, outputDir: string) {
const history = await client.history.getAll({ page_size: 1000 });
// Create backup directory
fs.mkdirSync(outputDir, { recursive: true });
// Save metadata
fs.writeFileSync(
path.join(outputDir, 'metadata.json'),
JSON.stringify(history, null, 2)
);
// Download all audio files
for (let i = 0; i < history.history.length; i++) {
const item = history.history[i];
try {
const audioStream = await client.history.getAudio(item.history_item_id);
const filename = `${item.history_item_id}_${item.voice_name?.replace(/\s+/g, '_') || 'unknown'}.mp3`;
await pipelineAsync(
audioStream,
fs.createWriteStream(path.join(outputDir, filename))
);
console.log(`Backed up ${i + 1}/${history.history.length}: ${filename}`);
} catch (error) {
console.error(`Failed to backup ${item.history_item_id}:`, error);
}
}
console.log(`Backup completed in ${outputDir}`);
}
// Usage
await backupHistory(client, './history_backup');import { ElevenLabsError, ElevenLabsTimeoutError } from 'elevenlabs';
try {
const history = await client.history.getAll({
page_size: 100,
search: "specific content"
});
for (const item of history.history) {
try {
const audio = await client.history.getAudio(item.history_item_id);
await processAudio(audio);
} catch (audioError) {
console.error(`Failed to get audio for ${item.history_item_id}:`, audioError);
continue; // Continue with other items
}
}
} catch (error) {
if (error instanceof ElevenLabsError) {
console.error('History API error:', error.statusCode);
if (error.statusCode === 401) {
console.error('Authentication error - check API key');
} else if (error.statusCode === 404) {
console.error('History item not found');
} else if (error.statusCode === 429) {
console.error('Rate limit exceeded');
}
} else if (error instanceof ElevenLabsTimeoutError) {
console.error('Request timed out:', error.message);
}
}// Process history in chunks to avoid memory issues
async function processHistoryInBatches(
client: ElevenLabsClient,
batchSize: number = 50,
processor: (items: HistoryItemResponse[]) => Promise<void>
) {
let startAfter: string | undefined;
let hasMore = true;
while (hasMore) {
const batch = await client.history.getAll({
page_size: batchSize,
start_after_history_item_id: startAfter
});
if (batch.history.length > 0) {
await processor(batch.history);
startAfter = batch.history[batch.history.length - 1].history_item_id;
hasMore = batch.has_more;
} else {
hasMore = false;
}
}
}
// Usage
await processHistoryInBatches(client, 25, async (items) => {
console.log(`Processing batch of ${items.length} items`);
// Process items...
});// Cache frequently accessed history items
class HistoryCache {
private cache = new Map<string, HistoryItemResponse>();
private audioCache = new Map<string, Buffer>();
async getHistoryItem(client: ElevenLabsClient, id: string): Promise<HistoryItemResponse> {
if (!this.cache.has(id)) {
const item = await client.history.get(id);
this.cache.set(id, item);
}
return this.cache.get(id)!;
}
async getHistoryAudio(client: ElevenLabsClient, id: string): Promise<stream.Readable> {
if (!this.audioCache.has(id)) {
const audioStream = await client.history.getAudio(id);
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
audioStream.on('data', chunk => chunks.push(chunk));
audioStream.on('end', () => {
const buffer = Buffer.concat(chunks);
this.audioCache.set(id, buffer);
resolve(stream.Readable.from(buffer));
});
audioStream.on('error', reject);
});
}
return stream.Readable.from(this.audioCache.get(id)!);
}
clear() {
this.cache.clear();
this.audioCache.clear();
}
}voice_id, source, search) to reduce data transfer