firecrawl/firecrawl · error · Exception

Failed to parse Firecrawl response as JSON.

Error message

Failed to parse Firecrawl response as JSON.

What it means

Raised by scrape_url when response.json() raises ValueError (Python's json decoder) on a 200 response. The body was not valid JSON, so the SDK cannot parse the scrape result.

Source

Thrown at apps/python-sdk/firecrawl/v1/client.py:686

        # Make request
        response = requests.post(
            f'{self.api_url}/v1/scrape',
            headers=_headers,
            json=scrape_params,
            timeout=(timeout / 1000.0 + 5 if timeout is not None else None)
        )

        if response.status_code == 200:
            try:
                response_json = response.json()
                if response_json.get('success') and 'data' in response_json:
                    return V1ScrapeResponse(**response_json['data'])
                elif "error" in response_json:
                    raise Exception(f'Failed to scrape URL. Error: {response_json["error"]}')
                else:
                    raise Exception(f'Failed to scrape URL. Error: {response_json}')
            except ValueError:
                raise Exception('Failed to parse Firecrawl response as JSON.')
        else:
            self._handle_error(response, 'scrape URL')

    def search(
            self,
            query: str,
            *,
            limit: Optional[int] = None,
            tbs: Optional[str] = None,
            filter: Optional[str] = None,
            lang: Optional[str] = None,
            country: Optional[str] = None,
            location: Optional[str] = None,
            timeout: Optional[int] = 30000,
            scrape_options: Optional[V1ScrapeOptions] = None,
            **kwargs) -> V1SearchResponse:
        """
        Search for content using Firecrawl.

View on GitHub (pinned to 656bffcc28)

Solutions

  1. Retry once - truncation and transient interstitials often clear on the next request.
  2. Inspect response.text indirectly by wrapping scrape_url and dumping the raw body before parsing fails.
  3. If using a proxy, verify it sets Accept: application/json and does not transform responses.
  4. Report to firecrawl if the body is consistently non-JSON from api.firecrawl.dev.

Example fix

// before
result = app.scrape_url(url)

// after
import time
for attempt in range(3):
    try:
        result = app.scrape_url(url)
        break
    except Exception as e:
        if "Failed to parse" in str(e) and attempt < 2:
            time.sleep(1)
            continue
        raise
Defensive patterns

Strategy: retry

Validate before calling

import requests
# pre-flight: confirm endpoint returns JSON
r = requests.head(f'{api_url}/v1/scrape', headers={'Authorization': f'Bearer {api_key}'})
assert 'json' in r.headers.get('content-type', ''), 'endpoint not serving JSON'

Type guard

def looks_like_json(text: str) -> bool:
    t = text.lstrip()[:1]
    return t in ('{', '[')

Try / catch

import time
for attempt in range(3):
    try:
        result = app.scrape_url(url)
        break
    except Exception as e:
        if 'Failed to parse' in str(e) and attempt < 2:
            time.sleep(2 ** attempt)
            continue
        raise

Prevention

When it happens

Trigger: The server returns 200 with HTML/text (e.g. a gateway error page, a CAPTCHA page, or an empty body); a proxy injects non-JSON content; the connection was truncated mid-body.

Common situations: Cloudflare interstitial served with 200; misconfigured reverse proxy returning plain-text errors; intermittent network drops that truncate the response.

Understand the failure class

Related errors


AI-assisted analysis of firecrawl/firecrawl@656bffcc28 (2026-08-12). Data as JSON: /api/errors/b241cabcb6515d35. Report an issue: GitHub.