History

(python/js/ts-sdks): readmes, e2e tests w idmux etc all good

rafaelmmiller committed 1 year ago

Commit 537f6c41 parent f4d904631 files · +788 −226Browse files

Showing the changes to apps/python-sdk (14 of 31 files). Show all

Unified · Split

14 files
  1. apps/python-sdk/.env.example +4 −1
  2. apps/python-sdk/README.md +44 −28
  3. apps/python-sdk/firecrawl/__tests__/e2e/v2/.env.example +5 −2
  4. apps/python-sdk/firecrawl/__tests__/e2e/v2/aio/test_aio_map.py +0 −2
  5. apps/python-sdk/firecrawl/__tests__/e2e/v2/conftest.py +73 −0
  6. apps/python-sdk/firecrawl/__tests__/e2e/v2/test_map.py +1 −4
  7. apps/python-sdk/firecrawl/__tests__/e2e/v2/test_search.py +0 −6
  8. apps/python-sdk/firecrawl/__tests__/e2e/v2/test_usage.py +1 −6
  9. apps/python-sdk/firecrawl/__tests__/e2e/v2/test_watcher.py +1 −0
  10. apps/python-sdk/firecrawl/client.py +39 −17
  11. apps/python-sdk/firecrawl/firecrawl.backup.py +0 −0
  12. apps/python-sdk/firecrawl/v2/client.py +128 −14
  13. apps/python-sdk/firecrawl/v2/methods/aio/crawl.py +1 −2
  14. apps/python-sdk/firecrawl/v2/methods/aio/usage.py +2 −2
apps/python-sdk/.env.example+4−1
@@ -1,2 +1,5 @@
11 FIRECRAWL_API_KEY=<your api key>
2−FIRECRAWL_API_URL=https://api.firecrawl.dev
2+FIRECRAWL_API_URL=https://api.firecrawl.dev
3+
4+# TESTING PURPOSE:
5+IDMUX_URL=
apps/python-sdk/README.md+44−28
@@ -13,24 +13,25 @@ pip install firecrawl-py · 12 unmodified lines
1313 ## Usage
1414
1515 1. Get an API key from [firecrawl.dev](https://firecrawl.dev)
16−2. Set the API key as an environment variable named `FIRECRAWL_API_KEY` or pass it as a parameter to the `FirecrawlApp` class.
16+2. Set the API key as an environment variable named `FIRECRAWL_API_KEY` or pass it as a parameter to the `Firecrawl` class.
1717
1818 Here's an example of how to use the SDK:
1919
2020 ```python
21−from firecrawl import FirecrawlApp, ScrapeOptions
21+from firecrawl import Firecrawl
22+from firecrawl.types import ScrapeOptions
2223
23−app = FirecrawlApp(api_key="fc-YOUR_API_KEY")
24+firecrawl = Firecrawl(api_key="fc-YOUR_API_KEY")
2425
25−# Scrape a website:
26−data = app.scrape_url(
26+# Scrape a website (v2):
27+data = firecrawl.scrape(
2728 'https://firecrawl.dev',
2829 formats=['markdown', 'html']
2930 )
3031 print(data)
3132
32−# Crawl a website:
33−crawl_status = app.crawl_url(
33+# Crawl a website (v2 waiter):
34+crawl_status = firecrawl.crawl(
3435 'https://firecrawl.dev',
3536 limit=100,
3637 scrape_options=ScrapeOptions(formats=['markdown', 'html'])
@@ -40,20 +41,20 @@ print(crawl_status) · 3 unmodified lines
4041
4142 ### Scraping a URL
4243
43−To scrape a single URL, use the `scrape_url` method. It takes the URL as a parameter and returns the scraped data as a dictionary.
44+To scrape a single URL, use the `scrape` method. It takes the URL as a parameter and returns a document with the requested formats.
4445
4546 ```python
46−# Scrape a website:
47−scrape_result = app.scrape_url('firecrawl.dev', formats=['markdown', 'html'])
47+# Scrape a website (v2):
48+scrape_result = firecrawl.scrape('https://firecrawl.dev', formats=['markdown', 'html'])
4849 print(scrape_result)
4950 ```
5051
5152 ### Crawling a Website
5253
53−To crawl a website, use the `crawl_url` method. It takes the starting URL and optional parameters as arguments. The `params` argument allows you to specify additional options for the crawl job, such as the maximum number of pages to crawl, allowed domains, and the output format.
54+To crawl a website, use the `crawl` method. It takes the starting URL and optional parameters as arguments. You can control depth, limits, formats, and more.
5455
5556 ```python
56−crawl_status = app.crawl_url(
57+crawl_status = firecrawl.crawl(
5758 'https://firecrawl.dev',
5859 limit=100,
5960 scrape_options=ScrapeOptions(formats=['markdown', 'html']),
@@ -66,23 +67,23 @@ print(crawl_status) · 6 unmodified lines
6667
6768 <Tip>Looking for async operations? Check out the [Async Class](#async-class) section below.</Tip>
6869
69−To crawl a website asynchronously, use the `crawl_url_async` method. It returns the crawl `ID` which you can use to check the status of the crawl job. It takes the starting URL and optional parameters as arguments. The `params` argument allows you to specify additional options for the crawl job, such as the maximum number of pages to crawl, allowed domains, and the output format.
70+To enqueue a crawl asynchronously, use `start_crawl`. It returns the crawl `ID` which you can use to check the status of the crawl job.
7071
7172 ```python
72−crawl_status = app.async_crawl_url(
73+crawl_job = firecrawl.start_crawl(
7374 'https://firecrawl.dev',
7475 limit=100,
7576 scrape_options=ScrapeOptions(formats=['markdown', 'html']),
7677 )
77−print(crawl_status)
78+print(crawl_job)
7879 ```
7980
8081 ### Checking Crawl Status
8182
82−To check the status of a crawl job, use the `check_crawl_status` method. It takes the job ID as a parameter and returns the current status of the crawl job.
83+To check the status of a crawl job, use the `get_crawl_status` method. It takes the job ID as a parameter and returns the current status of the crawl job.
8384
8485 ```python
85−crawl_status = app.check_crawl_status("<crawl_id>")
86+crawl_status = firecrawl.get_crawl_status("<crawl_id>")
8687 print(crawl_status)
8788 ```
8889
@@ -91,17 +92,17 @@ print(crawl_status) · 2 unmodified lines
9192 To cancel an asynchronous crawl job, use the `cancel_crawl` method. It takes the job ID of the asynchronous crawl as a parameter and returns the cancellation status.
9293
9394 ```python
94−cancel_crawl = app.cancel_crawl(id)
95+cancel_crawl = firecrawl.cancel_crawl(id)
9596 print(cancel_crawl)
9697 ```
9798
9899 ### Map a Website
99100
100−Use `map_url` to generate a list of URLs from a website. The `params` argument let you customize the mapping process, including options to exclude subdomains or to utilize the sitemap.
101+Use `map` to generate a list of URLs from a website. Options let you customize the mapping process, including whether to use the sitemap or include subdomains.
101102
102103 ```python
103−# Map a website:
104−map_result = app.map_url('https://firecrawl.dev')
104+# Map a website (v2):
105+map_result = firecrawl.map('https://firecrawl.dev')
105106 print(map_result)
106107 ```
107108
@@ -152,20 +153,35 @@ The SDK handles errors returned by the Firecrawl API and raises appropriate exce · 44 unmodified lines
152153
153154 ## Async Class
154155
155−For async operations, you can use the `AsyncFirecrawlApp` class. Its methods are the same as the `FirecrawlApp` class, but they don't block the main thread.
156+For async operations, you can use the `AsyncFirecrawl` class. Its methods mirror the `Firecrawl` class, but you `await` them.
156157
157158 ```python
158−from firecrawl import AsyncFirecrawlApp
159+from firecrawl import AsyncFirecrawl
159160
160−app = AsyncFirecrawlApp(api_key="YOUR_API_KEY")
161+firecrawl = AsyncFirecrawl(api_key="YOUR_API_KEY")
161162
162−# Async Scrape
163+# Async Scrape (v2)
163164 async def example_scrape():
164− scrape_result = await app.scrape_url(url="https://example.com")
165+ scrape_result = await firecrawl.scrape(url="https://example.com")
165166 print(scrape_result)
166167
167−# Async Crawl
168+# Async Crawl (v2)
168169 async def example_crawl():
169− crawl_result = await app.crawl_url(url="https://example.com")
170+ crawl_result = await firecrawl.crawl(url="https://example.com")
170171 print(crawl_result)
172+```
173+
174+## v1 compatibility
175+
176+For legacy code paths, v1 remains available under `firecrawl.v1` with the original method names.
177+
178+```python
179+from firecrawl import Firecrawl
180+
181+firecrawl = Firecrawl(api_key="YOUR_API_KEY")
182+
183+# v1 methods (feature‑frozen)
184+doc_v1 = firecrawl.v1.scrape_url('https://firecrawl.dev', formats=['markdown', 'html'])
185+crawl_v1 = firecrawl.v1.crawl_url('https://firecrawl.dev', limit=100)
186+map_v1 = firecrawl.v1.map_url('https://firecrawl.dev')
171187 ```
apps/python-sdk/firecrawl/__tests__/e2e/v2/.env.example+5−2
@@ -1,2 +1,5 @@
1−API_URL=http://localhost:3002
2−API_KEY=fc-YOUR_API_KEY
1+FIRECRAWL_API_KEY=<your api key>
2+FIRECRAWL_API_URL=https://api.firecrawl.dev
3+
4+# TESTING PURPOSE:
5+IDMUX_URL=
apps/python-sdk/firecrawl/__tests__/e2e/v2/aio/test_aio_map.py+0−2
@@ -17,7 +17,6 @@ if not os.getenv("API_URL"): · 16 unmodified lines
1717 async def test_async_map_minimal():
1818 client = AsyncFirecrawl(api_key=os.getenv("API_KEY"), api_url=os.getenv("API_URL"))
1919 resp = await client.v2.map("https://docs.firecrawl.dev")
20− assert resp.success is True
2120 assert hasattr(resp, "links") and isinstance(resp.links, list)
2221 if resp.links:
2322 first = resp.links[0]
@@ -36,7 +35,6 @@ async def test_async_map_with_all_params(sitemap): · 12 unmodified lines
3635 sitemap=sitemap,
3736 timeout=15000,
3837 )
39− assert resp.success is True
4038 assert hasattr(resp, "links") and isinstance(resp.links, list)
4139 assert len(resp.links) <= 10
4240
apps/python-sdk/firecrawl/__tests__/e2e/v2/conftest.py+73−0
@@ -0,0 +1,73 @@
1+import os
2+import json
3+import pytest
4+import requests
5+from dotenv import load_dotenv
6+
7+load_dotenv()
8+
9+def _idmux(identity_request: dict) -> dict:
10+ idmux_url = os.getenv("IDMUX_URL")
11+ if not idmux_url:
12+ raise EnvironmentError("IDMUX_URL is not set. E2E tests must use idmux for credentials.")
13+ run_number = int(os.getenv("GITHUB_RUN_NUMBER") or 0)
14+ payload = {
15+ "refName": os.getenv("GITHUB_REF_NAME") or "local",
16+ "runNumber": run_number,
17+ "concurrency": identity_request.get("concurrency", 100),
18+ **identity_request,
19+ }
20+ resp = requests.post(idmux_url + "/", json=payload)
21+ resp.raise_for_status()
22+ return resp.json()
23+
24+@pytest.fixture(scope="session")
25+def api_url():
26+ # Prefer TEST_URL, then FIRECRAWL_API_URL (for parity with JS), then legacy API_URL
27+ return (
28+ os.getenv("TEST_URL")
29+ or os.getenv("FIRECRAWL_API_URL")
30+ or os.getenv("API_URL")
31+ or "https://api.firecrawl.dev"
32+ )
33+
34+# Resolve identity and export environment at import time so tests that read env at module import succeed
35+_IDENTITY = None
36+_API_URL = (
37+ os.getenv("TEST_URL")
38+ or os.getenv("FIRECRAWL_API_URL")
39+ or os.getenv("API_URL")
40+ or "https://api.firecrawl.dev"
41+)
42+
43+_IDMUX_URL = os.getenv("IDMUX_URL")
44+if _IDMUX_URL:
45+ run_name = os.getenv("PYTEST_RUN_NAME") or "py-e2e"
46+ # If IDMUX_URL is set, idmux MUST succeed; do not silently fall back
47+ _IDENTITY = _idmux({"name": run_name})
48+ os.environ["API_KEY"] = _IDENTITY.get("apiKey", "")
49+ os.environ["API_URL"] = _API_URL
50+
51+@pytest.fixture(scope="session")
52+def api_identity():
53+ return _IDENTITY or {"apiKey": os.getenv("API_KEY") or "", "teamId": os.getenv("TEST_TEAM_ID") or os.getenv("TEAM_ID") or ""}
54+
55+@pytest.fixture(autouse=True)
56+def _inject_client(request, api_identity, api_url):
57+ # For class-based tests that rely on self.client, inject a client if missing
58+ inst = getattr(request, "instance", None)
59+ if inst is not None and not hasattr(inst, "client"):
60+ try:
61+ from firecrawl import Firecrawl
62+ inst.client = Firecrawl(api_key=api_identity.get("apiKey", ""), api_url=api_url)
63+ except Exception:
64+ pass
65+ # For function-based modules that expect a module-level `firecrawl` symbol
66+ mod = getattr(request, "module", None)
67+ if mod is not None and not hasattr(mod, "firecrawl"):
68+ try:
69+ from firecrawl import Firecrawl
70+ setattr(mod, "firecrawl", Firecrawl(api_key=api_identity.get("apiKey", ""), api_url=api_url))
71+ except Exception:
72+ pass
73+
apps/python-sdk/firecrawl/__tests__/e2e/v2/test_map.py+1−4
@@ -24,7 +24,6 @@ class TestMapE2E: · 23 unmodified lines
2424 def test_map_minimal_request(self):
2525 resp = self.client.map("https://docs.firecrawl.dev")
2626
27− assert hasattr(resp, "success") and resp.success is True
2827 assert hasattr(resp, "links") and resp.links is not None
2928 assert isinstance(resp.links, list)
3029
@@ -46,13 +45,11 @@ class TestMapE2E: · 15 unmodified lines
4645 resp = self.client.map(
4746 "https://docs.firecrawl.dev",
4847 search="docs",
49− include_subdomains=True,
50− limit=10,
48+ include_subdomains=True, limit=10,
5149 sitemap=sitemap,
5250 timeout=15000,
5351 )
5452
55− assert hasattr(resp, "success") and resp.success is True
5653 assert hasattr(resp, "links") and isinstance(resp.links, list)
5754
5855 # Limit should be respected (server-side)
apps/python-sdk/firecrawl/__tests__/e2e/v2/test_search.py+0−6
@@ -5,12 +5,6 @@ from firecrawl.types import SearchData, SearchResult, Document, ScrapeFormats, S · 4 unmodified lines
55
66 load_dotenv()
77
8−if not os.getenv("API_KEY"):
9− raise ValueError("API_KEY is not set")
10−
11−if not os.getenv("API_URL"):
12− raise ValueError("API_URL is not set")
13−
148 firecrawl = Firecrawl(api_key=os.getenv("API_KEY"), api_url=os.getenv("API_URL"))
159
1610 def _collect_texts(entries):
apps/python-sdk/firecrawl/__tests__/e2e/v2/test_usage.py+1−6
@@ -4,15 +4,10 @@ from firecrawl import Firecrawl · 3 unmodified lines
44
55 load_dotenv()
66
7−if not os.getenv("API_KEY"):
8− raise ValueError("API_KEY is not set")
9−
10−if not os.getenv("API_URL"):
11− raise ValueError("API_URL is not set")
12−
137
148 class TestUsageE2E:
159 def setup_method(self):
10+ # Environment is exported by conftest at import time
1611 self.client = Firecrawl(api_key=os.getenv("API_KEY"), api_url=os.getenv("API_URL"))
1712
1813 def test_get_concurrency(self):
apps/python-sdk/firecrawl/__tests__/e2e/v2/test_watcher.py+1−0
@@ -14,6 +14,7 @@ if not os.getenv("API_URL"): · 13 unmodified lines
1414
1515 class TestWatcherE2E:
1616 def setup_method(self):
17+ from firecrawl import Firecrawl
1718 self.client = Firecrawl(api_key=os.getenv("API_KEY"), api_url=os.getenv("API_URL"))
1819
1920 def test_crawl_watcher(self):
apps/python-sdk/firecrawl/client.py+39−17
@@ -113,20 +113,18 @@ class AsyncV2Proxy: · 112 unmodified lines
113113
114114 class Firecrawl:
115115 """
116− Unified Firecrawl client with version-specific access patterns.
117−
118− This class provides a unified interface that allows users to access
119− different API versions through version-specific attributes (v1, v2)
120− while defaulting to v2 for direct method calls.
116+ Unified Firecrawl client (v2 by default, v1 under ``.v1``).
117+
118+ Provides a single entrypoint that exposes the latest API directly while
119+ keeping a feature-frozen v1 available for incremental migration.
121120 """
122121
123122 def __init__(self, api_key: str = None, api_url: str = "https://api.firecrawl.dev"):
124− """
125− Initialize the unified Firecrawl client.
126−
123+ """Initialize the unified client.
124+
127125 Args:
128− api_key: Your Firecrawl API key
129− api_url: The Firecrawl API URL (defaults to production)
126+ api_key: Firecrawl API key (or set ``FIRECRAWL_API_KEY``)
127+ api_url: Base API URL (defaults to production)
130128 """
131129 self.api_key = api_key
132130 self.api_url = api_url
@@ -164,13 +162,7 @@ class Firecrawl: · 31 unmodified lines
164162 self.watcher = self._v2_client.watcher
165163
166164 class AsyncFirecrawl:
167− """
168− Unified Firecrawl client with version-specific access patterns.
169−
170− This class provides a unified interface that allows users to access
171− different API versions through version-specific attributes (v1, v2)
172− while defaulting to v2 for direct method calls.
173− """
165+ """Async unified Firecrawl client (v2 by default, v1 under ``.v1``)."""
174166
175167 def __init__(self, api_key: str = None, api_url: str = "https://api.firecrawl.dev"):
176168 self.api_key = api_key
@@ -185,6 +177,36 @@ class AsyncFirecrawl: · 8 unmodified lines
185177 self.v1 = AsyncV1Proxy(self._v1_client) if self._v1_client else None
186178 self.v2 = AsyncV2Proxy(self._v2_client)
187179
180+ # Expose v2 async surface directly on the top-level client for ergonomic access
181+ # Keep method names aligned with the sync client
182+ self.scrape = self._v2_client.scrape
183+ self.search = self._v2_client.search
184+ self.map = self._v2_client.map
185+
186+ self.start_crawl = self._v2_client.start_crawl
187+ self.get_crawl_status = self._v2_client.get_crawl_status
188+ self.cancel_crawl = self._v2_client.cancel_crawl
189+ self.crawl = self._v2_client.crawl
190+ self.get_crawl_errors = self._v2_client.get_crawl_errors
191+ self.active_crawls = self._v2_client.active_crawls
192+ self.crawl_params_preview = self._v2_client.crawl_params_preview
193+
194+ self.start_batch_scrape = self._v2_client.start_batch_scrape
195+ self.get_batch_scrape_status = self._v2_client.get_batch_scrape_status
196+ self.cancel_batch_scrape = self._v2_client.cancel_batch_scrape
197+ self.batch_scrape = self._v2_client.batch_scrape
198+ self.get_batch_scrape_errors = self._v2_client.get_batch_scrape_errors
199+
200+ self.start_extract = self._v2_client.start_extract
201+ self.get_extract_status = self._v2_client.get_extract_status
202+ self.extract = self._v2_client.extract
203+
204+ self.get_concurrency = self._v2_client.get_concurrency
205+ self.get_credit_usage = self._v2_client.get_credit_usage
206+ self.get_token_usage = self._v2_client.get_token_usage
207+
208+ self.watcher = self._v2_client.watcher
209+
188210 # Export Firecrawl as an alias for FirecrawlApp
189211 FirecrawlApp = Firecrawl
190212 AsyncFirecrawlApp = AsyncFirecrawl
apps/python-sdk/firecrawl/firecrawl.backup.pyrenamed from apps/python-sdk/firecrawl/DELETE_firecrawl.py+0−0

File renamed without changes.

apps/python-sdk/firecrawl/v2/client.py+128−14
@@ -408,8 +408,18 @@ class FirecrawlClient: · 407 unmodified lines
408408 sitemap: Optional[Literal["only", "include", "skip"]] = None,
409409 timeout: Optional[int] = None,
410410 ) -> MapData:
411− """
412− Map a URL and return discovered links (with optional titles/descriptions).
411+ """Map a URL and return discovered links.
412+
413+ Args:
414+ url: Root URL to explore
415+ search: Optional substring filter for discovered links
416+ include_subdomains: Whether to include subdomains
417+ limit: Maximum number of links to return
418+ sitemap: Sitemap usage mode ("only" | "include" | "skip")
419+ timeout: Request timeout in milliseconds
420+
421+ Returns:
422+ MapData containing the discovered links
413423 """
414424 options = MapOptions(
415425 search=search,
@@ -434,8 +444,14 @@ class FirecrawlClient: · 18 unmodified lines
434444 return crawl_module.cancel_crawl(self.http_client, crawl_id)
435445
436446 def crawl_params_preview(self, url: str, prompt: str) -> CrawlParamsData:
437− """
438− Get crawl parameters from LLM based on URL and prompt.
447+ """Derive crawl parameters from natural-language prompt.
448+
449+ Args:
450+ url: Root URL
451+ prompt: Instruction describing how to crawl
452+
453+ Returns:
454+ CrawlParamsData with normalized crawl configuration
439455 """
440456 request = CrawlParamsRequest(url=url, prompt=prompt)
441457 return crawl_module.crawl_params_preview(self.http_client, request)
@@ -453,8 +469,21 @@ class FirecrawlClient: · 11 unmodified lines
453469 scrape_options: Optional['ScrapeOptions'] = None,
454470 ignore_invalid_urls: Optional[bool] = None,
455471 ):
456− """
457− Start an extract job (non-blocking). Use get_extract_status to poll.
472+ """Start an extract job (non-blocking).
473+
474+ Args:
475+ urls: URLs to extract from (optional)
476+ prompt: Natural-language instruction for extraction
477+ schema: Target JSON schema for the output
478+ system_prompt: Optional system instruction
479+ allow_external_links: Allow hyperlinks in output
480+ enable_web_search: Whether to augment with web search
481+ show_sources: Include per-field/source mapping when available
482+ scrape_options: Scrape options applied prior to extraction
483+ ignore_invalid_urls: Skip invalid URLs instead of failing
484+
485+ Returns:
486+ Response payload with job id/status (poll with get_extract_status)
458487 """
459488 return extract_module.start_extract(
460489 self.http_client,
@@ -484,8 +513,23 @@ class FirecrawlClient: · 23 unmodified lines
484513 poll_interval: int = 2,
485514 timeout: Optional[int] = None,
486515 ):
487− """
488− Extract structured data using the v2 client surface, waiting until completion.
516+ """Extract structured data and wait until completion.
517+
518+ Args:
519+ urls: URLs to extract from (optional)
520+ prompt: Natural-language instruction for extraction
521+ schema: Target JSON schema for the output
522+ system_prompt: Optional system instruction
523+ allow_external_links: Allow hyperlinks in output
524+ enable_web_search: Whether to augment with web search
525+ show_sources: Include per-field/source mapping when available
526+ scrape_options: Scrape options applied prior to extraction
527+ ignore_invalid_urls: Skip invalid URLs instead of failing
528+ poll_interval: Seconds between status checks
529+ timeout: Maximum seconds to wait (None for no timeout)
530+
531+ Returns:
532+ Final extract response when completed
489533 """
490534 return extract_module.extract(
491535 self.http_client,
@@ -533,8 +577,39 @@ class FirecrawlClient: · 41 unmodified lines
533577 integration: Optional[str] = None,
534578 idempotency_key: Optional[str] = None,
535579 ):
536− """
537− Start a batch scrape job over multiple URLs.
580+ """Start a batch scrape job over multiple URLs (non-blocking).
581+
582+ Args:
583+ urls: List of URLs to scrape
584+ formats: Output formats to collect per URL
585+ headers: HTTP headers
586+ include_tags: HTML tags to include
587+ exclude_tags: HTML tags to exclude
588+ only_main_content: Restrict scraping to main content
589+ timeout: Per-request timeout in milliseconds
590+ wait_for: Wait condition in milliseconds
591+ mobile: Emulate mobile viewport
592+ parsers: Parser list (e.g., ["pdf"])
593+ actions: Browser actions to perform
594+ location: Location settings
595+ skip_tls_verification: Skip TLS verification
596+ remove_base64_images: Remove base64 images from output
597+ fast_mode: Prefer faster scraping modes
598+ use_mock: Use a mock data source (internal/testing)
599+ block_ads: Block ads during scraping
600+ proxy: Proxy setting
601+ max_age: Cache max age
602+ store_in_cache: Whether to store results in cache
603+ webhook: Webhook configuration
604+ append_to_id: Append to an existing batch job
605+ ignore_invalid_urls: Skip invalid URLs without failing
606+ max_concurrency: Max concurrent scrapes
607+ zero_data_retention: Delete data after 24 hours
608+ integration: Integration tag/name
609+ idempotency_key: Header used to deduplicate starts
610+
611+ Returns:
612+ Response payload with job id (poll with get_batch_scrape_status)
538613 """
539614 options = ScrapeOptions(
540615 **{k: v for k, v in dict(
@@ -574,18 +649,47 @@ class FirecrawlClient: · 33 unmodified lines
574649 )
575650
576651 def get_batch_scrape_status(self, job_id: str):
577− """Get current status and any scraped data for a batch scrape job."""
652+ """Get current status and any scraped data for a batch job.
653+
654+ Args:
655+ job_id: Batch job ID
656+
657+ Returns:
658+ Status payload including counts and partial data
659+ """
578660 return batch_module.get_batch_scrape_status(self.http_client, job_id)
579661
580662 def cancel_batch_scrape(self, job_id: str) -> bool:
581− """Cancel a running batch scrape job."""
663+ """Cancel a running batch scrape job.
664+
665+ Args:
666+ job_id: Batch job ID
667+
668+ Returns:
669+ True if the job was cancelled
670+ """
582671 return batch_module.cancel_batch_scrape(self.http_client, job_id)
583672
584673 def get_batch_scrape_errors(self, job_id: str):
585− """Retrieve error details for a batch scrape job."""
674+ """Retrieve error details for a batch scrape job.
675+
676+ Args:
677+ job_id: Batch job ID
678+
679+ Returns:
680+ Errors and robots-blocked URLs for the job
681+ """
586682 return batch_methods.get_batch_scrape_errors(self.http_client, job_id)
587683
588684 def get_extract_status(self, job_id: str):
685+ """Get the current status (and data if completed) of an extract job.
686+
687+ Args:
688+ job_id: Extract job ID
689+
690+ Returns:
691+ Extract response payload with status and optional data
692+ """
589693 return extract_module.get_extract_status(self.http_client, job_id)
590694
591695 def get_concurrency(self):
@@ -608,7 +712,17 @@ class FirecrawlClient: · 16 unmodified lines
608712 poll_interval: int = 2,
609713 timeout: Optional[int] = None,
610714 ) -> Watcher:
611− """Create a polling-based watcher for crawl or batch jobs."""
715+ """Create a watcher for crawl or batch jobs.
716+
717+ Args:
718+ job_id: Job ID to watch
719+ kind: Job kind ("crawl" or "batch")
720+ poll_interval: Seconds between status checks
721+ timeout: Maximum seconds to watch (None for no timeout)
722+
723+ Returns:
724+ Watcher instance
725+ """
612726 return Watcher(self, job_id, kind=kind, poll_interval=poll_interval, timeout=timeout)
613727
614728 def batch_scrape(
apps/python-sdk/firecrawl/v2/methods/aio/crawl.py+1−2
@@ -160,8 +160,7 @@ async def get_active_crawls(client: AsyncHttpClient) -> ActiveCrawlsResponse: · 159 unmodified lines
160160 body = response.json()
161161 if not body.get("success"):
162162 raise Exception(body.get("error", "Unknown error occurred"))
163− data = body.get("data", {})
164− crawls_in = data.get("crawls", [])
163+ crawls_in = body.get("crawls", [])
165164 normalized = []
166165 for c in crawls_in:
167166 if isinstance(c, dict):
apps/python-sdk/firecrawl/v2/methods/aio/usage.py+2−2
@@ -18,7 +18,7 @@ async def get_concurrency(client: AsyncHttpClient) -> ConcurrencyCheck: · 17 unmodified lines
1818
1919
2020 async def get_credit_usage(client: AsyncHttpClient) -> CreditUsage:
21− resp = await client.get("/v2/team/credit-usage")
21+ resp = await client.get("/v2/credit-usage")
2222 if resp.status_code >= 400:
2323 handle_response_error(resp, "get credit usage")
2424 body = resp.json()
@@ -29,7 +29,7 @@ async def get_credit_usage(client: AsyncHttpClient) -> CreditUsage: · 4 unmodified lines
2929
3030
3131 async def get_token_usage(client: AsyncHttpClient) -> TokenUsage:
32− resp = await client.get("/v2/team/token-usage")
32+ resp = await client.get("/v2/token-usage")
3333 if resp.status_code >= 400:
3434 handle_response_error(resp, "get token usage")
3535 body = resp.json()