0
# Legacy V1 API
1
2
Feature-frozen v1 API with additional capabilities including deep research, LLMs.txt generation, and WebSocket monitoring.
3
4
## Accessing V1 API
5
6
```typescript
7
// Access v1 client through unified client
8
const app = new Firecrawl({ apiKey: 'your-api-key' });
9
const v1Client = app.v1;
10
11
// Or instantiate v1 client directly
12
import { FirecrawlAppV1 } from '@mendable/firecrawl-js';
13
const v1Client = new FirecrawlAppV1({ apiKey: 'your-api-key' });
14
```
15
16
## V1 Client Configuration
17
18
```typescript { .api }
19
interface FirecrawlAppConfig {
20
apiKey?: string | null;
21
apiUrl?: string | null;
22
}
23
24
class FirecrawlApp {
25
constructor(config: FirecrawlAppConfig);
26
}
27
```
28
29
## Core V1 Methods
30
31
### Scraping
32
33
```typescript { .api }
34
/**
35
* Scrape a single URL with v1 API
36
* @param url - URL to scrape
37
* @param params - V1 scraping parameters
38
* @returns Promise resolving to scrape response
39
*/
40
scrapeUrl<T extends ZodSchema, ActionsSchema extends Action[] | undefined>(
41
url: string,
42
params?: ScrapeParams<T, ActionsSchema>
43
): Promise<ScrapeResponse<infer<T>, ActionsSchema extends Action[] ? ActionsResult : never> | ErrorResponse>;
44
```
45
46
### Search
47
48
```typescript { .api }
49
/**
50
* Search using v1 API
51
* @param query - Search query
52
* @param params - V1 search parameters
53
* @returns Promise resolving to search results
54
*/
55
search(query: string, params?: SearchParams): Promise<SearchResponse>;
56
```
57
58
### Crawling
59
60
```typescript { .api }
61
/**
62
* Crawl URL with v1 API (with polling)
63
* @param url - URL to crawl
64
* @param params - V1 crawl parameters
65
* @param pollInterval - Polling interval in seconds
66
* @param idempotencyKey - Optional idempotency key
67
* @returns Promise resolving to crawl status
68
*/
69
crawlUrl(
70
url: string,
71
params?: CrawlParams,
72
pollInterval?: number,
73
idempotencyKey?: string
74
): Promise<CrawlStatusResponse | ErrorResponse>;
75
76
/**
77
* Start async crawl without polling
78
* @param url - URL to crawl
79
* @param params - V1 crawl parameters
80
* @param idempotencyKey - Optional idempotency key
81
* @returns Promise resolving to crawl initiation response
82
*/
83
asyncCrawlUrl(
84
url: string,
85
params?: CrawlParams,
86
idempotencyKey?: string
87
): Promise<CrawlResponse | ErrorResponse>;
88
89
/**
90
* Check crawl status
91
* @param id - Crawl job ID
92
* @param getAllData - Get all pages of data
93
* @param nextURL - Next URL for pagination
94
* @param skip - Skip entries for pagination
95
* @param limit - Limit entries returned
96
* @returns Promise resolving to crawl status
97
*/
98
checkCrawlStatus(
99
id?: string,
100
getAllData?: boolean,
101
nextURL?: string,
102
skip?: number,
103
limit?: number
104
): Promise<CrawlStatusResponse | ErrorResponse>;
105
106
/**
107
* Cancel crawl job
108
* @param id - Crawl job ID
109
* @returns Promise resolving to cancellation response
110
*/
111
cancelCrawl(id: string): Promise<ErrorResponse>;
112
113
/**
114
* Get crawl errors
115
* @param id - Crawl job ID
116
* @returns Promise resolving to error information
117
*/
118
checkCrawlErrors(id: string): Promise<CrawlErrorsResponse | ErrorResponse>;
119
120
/**
121
* Crawl with WebSocket monitoring
122
* @param url - URL to crawl
123
* @param params - V1 crawl parameters
124
* @param idempotencyKey - Optional idempotency key
125
* @returns Promise resolving to CrawlWatcher instance
126
*/
127
crawlUrlAndWatch(
128
url: string,
129
params?: CrawlParams,
130
idempotencyKey?: string
131
): Promise<CrawlWatcher>;
132
```
133
134
### Batch Operations
135
136
```typescript { .api }
137
/**
138
* Batch scrape URLs with v1 API (with polling)
139
* @param urls - URLs to scrape
140
* @param params - V1 scrape parameters
141
* @param pollInterval - Polling interval in seconds
142
* @param idempotencyKey - Optional idempotency key
143
* @param webhook - Optional webhook configuration
144
* @param ignoreInvalidURLs - Ignore invalid URLs
145
* @param maxConcurrency - Maximum concurrent requests
146
* @returns Promise resolving to batch status
147
*/
148
batchScrapeUrls(
149
urls: string[],
150
params?: ScrapeParams,
151
pollInterval?: number,
152
idempotencyKey?: string,
153
webhook?: CrawlParams["webhook"],
154
ignoreInvalidURLs?: boolean,
155
maxConcurrency?: number
156
): Promise<BatchScrapeStatusResponse | ErrorResponse>;
157
158
/**
159
* Start async batch scrape without polling
160
* @param urls - URLs to scrape
161
* @param params - V1 scrape parameters
162
* @param idempotencyKey - Optional idempotency key
163
* @param webhook - Optional webhook configuration
164
* @param ignoreInvalidURLs - Ignore invalid URLs
165
* @returns Promise resolving to batch initiation response
166
*/
167
asyncBatchScrapeUrls(
168
urls: string[],
169
params?: ScrapeParams,
170
idempotencyKey?: string,
171
webhook?: CrawlParams["webhook"],
172
ignoreInvalidURLs?: boolean
173
): Promise<BatchScrapeResponse | ErrorResponse>;
174
175
/**
176
* Check batch scrape status
177
* @param id - Batch job ID
178
* @param getAllData - Get all pages of data
179
* @param nextURL - Next URL for pagination
180
* @param skip - Skip entries for pagination
181
* @param limit - Limit entries returned
182
* @returns Promise resolving to batch status
183
*/
184
checkBatchScrapeStatus(
185
id?: string,
186
getAllData?: boolean,
187
nextURL?: string,
188
skip?: number,
189
limit?: number
190
): Promise<BatchScrapeStatusResponse | ErrorResponse>;
191
192
/**
193
* Get batch scrape errors
194
* @param id - Batch job ID
195
* @returns Promise resolving to error information
196
*/
197
checkBatchScrapeErrors(id: string): Promise<CrawlErrorsResponse | ErrorResponse>;
198
199
/**
200
* Batch scrape with WebSocket monitoring
201
* @param urls - URLs to scrape
202
* @param params - V1 scrape parameters
203
* @param idempotencyKey - Optional idempotency key
204
* @param webhook - Optional webhook configuration
205
* @param ignoreInvalidURLs - Ignore invalid URLs
206
* @returns Promise resolving to CrawlWatcher instance
207
*/
208
batchScrapeUrlsAndWatch(
209
urls: string[],
210
params?: ScrapeParams,
211
idempotencyKey?: string,
212
webhook?: CrawlParams["webhook"],
213
ignoreInvalidURLs?: boolean
214
): Promise<CrawlWatcher>;
215
```
216
217
### Data Extraction
218
219
```typescript { .api }
220
/**
221
* Extract data with v1 API (with polling)
222
* @param urls - URLs to extract from
223
* @param params - V1 extraction parameters
224
* @returns Promise resolving to extraction results
225
*/
226
extract<T extends ZodSchema>(
227
urls?: string[],
228
params?: ExtractParams<T>
229
): Promise<ExtractResponse<infer<T>> | ErrorResponse>;
230
231
/**
232
* Start async extraction without polling
233
* @param urls - URLs to extract from
234
* @param params - V1 extraction parameters
235
* @param idempotencyKey - Optional idempotency key
236
* @returns Promise resolving to extraction initiation response
237
*/
238
asyncExtract(
239
urls: string[],
240
params?: ExtractParams,
241
idempotencyKey?: string
242
): Promise<ExtractResponse | ErrorResponse>;
243
244
/**
245
* Get extraction status
246
* @param jobId - Extraction job ID
247
* @returns Promise resolving to extraction status
248
*/
249
getExtractStatus(jobId: string): Promise<any>;
250
```
251
252
### Site Mapping
253
254
```typescript { .api }
255
/**
256
* Map URLs with v1 API
257
* @param url - URL to map
258
* @param params - V1 mapping parameters
259
* @returns Promise resolving to mapping results
260
*/
261
mapUrl(url: string, params?: MapParams): Promise<MapResponse | ErrorResponse>;
262
```
263
264
## V1 Exclusive Features
265
266
### Deep Research
267
268
```typescript { .api }
269
/**
270
* Perform deep research with v1 API (with polling)
271
* @param query - Research query
272
* @param params - Deep research parameters
273
* @param onActivity - Activity callback
274
* @param onSource - Source callback
275
* @returns Promise resolving to research results
276
*/
277
deepResearch(
278
query: string,
279
params: DeepResearchParams<ZodSchema>,
280
onActivity?: (activity: {
281
type: string;
282
status: string;
283
message: string;
284
timestamp: string;
285
depth: number;
286
}) => void,
287
onSource?: (source: {
288
url: string;
289
title?: string;
290
description?: string;
291
icon?: string;
292
}) => void
293
): Promise<DeepResearchStatusResponse | ErrorResponse>;
294
295
/**
296
* Start async deep research without polling
297
* @param query - Research query
298
* @param params - Deep research parameters
299
* @returns Promise resolving to research initiation response
300
*/
301
asyncDeepResearch(
302
query: string,
303
params: DeepResearchParams<ZodSchema>
304
): Promise<DeepResearchResponse | ErrorResponse>;
305
306
/**
307
* Check deep research status
308
* @param id - Research job ID
309
* @returns Promise resolving to research status
310
*/
311
checkDeepResearchStatus(id: string): Promise<DeepResearchStatusResponse | ErrorResponse>;
312
```
313
314
### LLMs.txt Generation
315
316
```typescript { .api }
317
/**
318
* Generate LLMs.txt with v1 API (with polling)
319
* @param url - URL to generate LLMs.txt from
320
* @param params - Generation parameters
321
* @returns Promise resolving to generation results
322
*/
323
generateLLMsText(
324
url: string,
325
params?: GenerateLLMsTextParams
326
): Promise<GenerateLLMsTextStatusResponse | ErrorResponse>;
327
328
/**
329
* Start async LLMs.txt generation without polling
330
* @param url - URL to generate LLMs.txt from
331
* @param params - Generation parameters
332
* @returns Promise resolving to generation initiation response
333
*/
334
asyncGenerateLLMsText(
335
url: string,
336
params?: GenerateLLMsTextParams
337
): Promise<GenerateLLMsTextResponse | ErrorResponse>;
338
339
/**
340
* Check LLMs.txt generation status
341
* @param id - Generation job ID
342
* @returns Promise resolving to generation status
343
*/
344
checkGenerateLLMsTextStatus(id: string): Promise<GenerateLLMsTextStatusResponse | ErrorResponse>;
345
```
346
347
## V1 Configuration Types
348
349
```typescript { .api }
350
// V1 scrape parameters
351
interface ScrapeParams<LLMSchema extends ZodSchema, ActionsSchema extends Action[] | undefined> {
352
formats?: string[];
353
headers?: Record<string, string>;
354
includeTags?: string[];
355
excludeTags?: string[];
356
onlyMainContent?: boolean;
357
waitFor?: number;
358
timeout?: number;
359
location?: LocationConfig;
360
mobile?: boolean;
361
skipTlsVerification?: boolean;
362
removeBase64Images?: boolean;
363
blockAds?: boolean;
364
proxy?: "basic" | "stealth" | "auto";
365
storeInCache?: boolean;
366
maxAge?: number;
367
parsePDF?: boolean;
368
extract?: {
369
prompt?: string;
370
schema?: LLMSchema;
371
systemPrompt?: string;
372
};
373
jsonOptions?: {
374
prompt?: string;
375
schema?: LLMSchema;
376
systemPrompt?: string;
377
};
378
changeTrackingOptions?: {
379
prompt?: string;
380
schema?: any;
381
modes?: ("json" | "git-diff")[];
382
tag?: string | null;
383
};
384
actions?: ActionsSchema;
385
agent?: AgentOptions;
386
zeroDataRetention?: boolean;
387
}
388
389
// V1 crawl parameters
390
interface CrawlParams {
391
includePaths?: string[];
392
excludePaths?: string[];
393
maxDepth?: number;
394
maxDiscoveryDepth?: number;
395
limit?: number;
396
allowBackwardLinks?: boolean;
397
crawlEntireDomain?: boolean;
398
allowExternalLinks?: boolean;
399
ignoreSitemap?: boolean;
400
scrapeOptions?: CrawlScrapeOptions;
401
webhook?: string | {
402
url: string;
403
headers?: Record<string, string>;
404
metadata?: Record<string, string>;
405
events?: ("completed" | "failed" | "page" | "started")[];
406
};
407
deduplicateSimilarURLs?: boolean;
408
ignoreQueryParameters?: boolean;
409
regexOnFullURL?: boolean;
410
delay?: number;
411
allowSubdomains?: boolean;
412
maxConcurrency?: number;
413
zeroDataRetention?: boolean;
414
}
415
416
// Deep research parameters
417
interface DeepResearchParams<LLMSchema extends ZodSchema> {
418
maxDepth?: number; // 1-10, default 7
419
timeLimit?: number; // 30-300 seconds, default 270
420
maxUrls?: number; // 1-1000, default 20
421
analysisPrompt?: string;
422
systemPrompt?: string;
423
formats?: ("markdown" | "json")[];
424
jsonOptions?: {
425
prompt?: string;
426
schema?: LLMSchema;
427
systemPrompt?: string;
428
};
429
}
430
431
// LLMs.txt generation parameters
432
interface GenerateLLMsTextParams {
433
maxUrls?: number; // 1-100, default 10
434
showFullText?: boolean; // default false
435
cache?: boolean; // default true
436
__experimental_stream?: boolean;
437
}
438
```
439
440
## V1 WebSocket Monitoring
441
442
```typescript { .api }
443
/**
444
* V1 WebSocket-based job watcher
445
*/
446
class CrawlWatcher extends TypedEventTarget<CrawlWatcherEvents> {
447
constructor(id: string, app: FirecrawlApp);
448
449
// Event listeners
450
on(event: 'document', listener: (e: CustomEvent<FirecrawlDocument>) => void): void;
451
on(event: 'done', listener: (e: CustomEvent<{
452
status: string;
453
data: FirecrawlDocument[];
454
}>) => void): void;
455
on(event: 'error', listener: (e: CustomEvent<{
456
status: string;
457
data: FirecrawlDocument[];
458
error: string;
459
}>) => void): void;
460
461
close(): void;
462
}
463
```
464
465
## Usage Examples
466
467
### V1 Deep Research
468
469
```typescript
470
const v1Client = app.v1;
471
472
// Perform comprehensive research with real-time updates
473
const researchResult = await v1Client.deepResearch(
474
'artificial intelligence safety research 2024',
475
{
476
maxDepth: 5,
477
timeLimit: 180, // 3 minutes
478
maxUrls: 30,
479
analysisPrompt: 'Analyze the current state of AI safety research, key developments, and future challenges',
480
formats: ['markdown', 'json'],
481
jsonOptions: {
482
schema: {
483
type: 'object',
484
properties: {
485
keyFindings: { type: 'array', items: { type: 'string' } },
486
researchers: { type: 'array', items: { type: 'string' } },
487
developments: { type: 'array', items: { type: 'object' } },
488
challenges: { type: 'array', items: { type: 'string' } },
489
recommendations: { type: 'array', items: { type: 'string' } }
490
}
491
}
492
}
493
},
494
// Activity callback
495
(activity) => {
496
console.log(`Research activity: ${activity.message} (depth: ${activity.depth})`);
497
},
498
// Source callback
499
(source) => {
500
console.log(`Found source: ${source.title} - ${source.url}`);
501
}
502
);
503
504
console.log('Research completed:', researchResult.data);
505
```
506
507
### V1 LLMs.txt Generation
508
509
```typescript
510
const v1Client = app.v1;
511
512
// Generate LLMs.txt for a website
513
const llmsResult = await v1Client.generateLLMsText('https://docs.example.com', {
514
maxUrls: 50,
515
showFullText: true,
516
cache: true
517
});
518
519
console.log('LLMs.txt generated:');
520
console.log(llmsResult.data.llmstxt);
521
522
if (llmsResult.data.llmsfulltxt) {
523
console.log('\nFull text version available');
524
}
525
```
526
527
### V1 Advanced Scraping with Actions
528
529
```typescript
530
const v1Client = app.v1;
531
532
// Scrape with browser automation
533
const scrapeResult = await v1Client.scrapeUrl('https://app.example.com', {
534
formats: ['markdown', 'json'],
535
actions: [
536
{ type: 'wait', selector: '#content' },
537
{ type: 'click', selector: '.load-more-button' },
538
{ type: 'wait', milliseconds: 3000 },
539
{ type: 'screenshot', fullPage: true },
540
{ type: 'scrape' }
541
],
542
jsonOptions: {
543
prompt: 'Extract product information including names, prices, and descriptions',
544
schema: {
545
type: 'object',
546
properties: {
547
products: {
548
type: 'array',
549
items: {
550
type: 'object',
551
properties: {
552
name: { type: 'string' },
553
price: { type: 'number' },
554
description: { type: 'string' }
555
}
556
}
557
}
558
}
559
}
560
}
561
});
562
563
console.log('Scraped data:', scrapeResult.data);
564
console.log('Actions performed:', scrapeResult.actions);
565
```
566
567
### V1 Crawl with WebSocket Monitoring
568
569
```typescript
570
const v1Client = app.v1;
571
572
// Start crawl with WebSocket monitoring
573
const watcher = await v1Client.crawlUrlAndWatch('https://example.com', {
574
limit: 100,
575
scrapeOptions: {
576
formats: ['markdown'],
577
onlyMainContent: true
578
}
579
});
580
581
// Listen for real-time updates
582
watcher.addEventListener('document', (event) => {
583
const document = event.detail;
584
console.log(`New document: ${document.url}`);
585
});
586
587
watcher.addEventListener('done', (event) => {
588
const { status, data } = event.detail;
589
console.log(`Crawl ${status}! Got ${data.length} documents`);
590
watcher.close();
591
});
592
593
watcher.addEventListener('error', (event) => {
594
const { error } = event.detail;
595
console.error(`Crawl error: ${error}`);
596
watcher.close();
597
});
598
```
599
600
### V1 Usage Analytics
601
602
```typescript
603
const v1Client = app.v1;
604
605
// Get v1 usage information (different format from v2)
606
const [creditUsage, tokenUsage, queueStatus] = await Promise.all([
607
v1Client.getCreditUsage(),
608
v1Client.getTokenUsage(),
609
v1Client.getQueueStatus()
610
]);
611
612
console.log('V1 Credit Usage:', {
613
remaining: creditUsage.data.remaining_credits,
614
plan: creditUsage.data.plan_credits,
615
billingStart: creditUsage.data.billing_period_start,
616
billingEnd: creditUsage.data.billing_period_end
617
});
618
619
console.log('V1 Token Usage:', {
620
remaining: tokenUsage.data.remaining_tokens,
621
plan: tokenUsage.data.plan_tokens
622
});
623
624
console.log('Queue Status:', queueStatus);
625
```
626
627
## Migration from V1 to V2
628
629
When migrating from v1 to v2 API, note these key differences:
630
631
1. **Naming conventions**: v2 uses camelCase consistently
632
2. **Response formats**: v2 has simplified response structures
633
3. **Type safety**: v2 provides better TypeScript integration
634
4. **Async patterns**: v2 uses modern async/await patterns throughout
635
5. **Missing features**: Deep research and LLMs.txt are v1-only
636
6. **WebSocket handling**: v2 uses EventEmitter-based watchers
637
638
For maximum compatibility, use the unified `Firecrawl` client which provides both APIs.