koala73/worldmonitor · error

P0 scrape failed: HTTP ${resp.status}

Error message

P0 scrape failed: HTTP ${resp.status}

What it means

P0Provider.fetch() throws when POST {P0_BASE_URL}/scrape (default https://api.parallelai.dev/v1/scrape) returns a non-2xx status. P0 is the JS-rendering, anti-bot, premium-proxy scraping provider; the request sends x-api-key auth with render_js:true, premium_proxy:true, and an AbortSignal deadline 10s above the render budget. Any HTTP-level rejection — bad key, quota, malformed body, provider outage — surfaces here with the raw status code.

Source

Thrown at consumer-prices-core/src/acquisition/p0.ts:54

    };
  }

  async fetch(url: string, opts: FetchOptions = {}): Promise<FetchResult> {
    const resp = await fetch(`${this.baseUrl}/scrape`, {
      method: 'POST',
      headers: this.headers(),
      body: JSON.stringify({
        url,
        render_js: true,
        wait_for: opts.waitForSelector,
        timeout: Math.floor((opts.timeout ?? 30_000) / 1_000),
        output_format: 'html',
        premium_proxy: true,
      }),
      signal: AbortSignal.timeout((opts.timeout ?? 30_000) + 10_000),
    });

    if (!resp.ok) throw new Error(`P0 scrape failed: HTTP ${resp.status}`);

    const data = (await resp.json()) as P0ScrapeResponse;
    if (!data.success && !data.html) {
      throw new Error(`P0 error: ${data.error ?? 'no content'}`);
    }

    return {
      url,
      html: data.html ?? '',
      markdown: data.markdown,
      statusCode: data.statusCode ?? 200,
      provider: this.name,
      fetchedAt: new Date(),
    };
  }

  async search(query: string, opts: SearchOptions = {}): Promise<SearchResult[]> {
    const resp = await fetch(`${this.baseUrl}/search`, {

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Map the status: 401/403 fix P0_API_KEY, 402/429 check plan/quota, 5xx retry later
  2. Set acquisition.fallback (e.g. 'playwright' or 'firecrawl') in the retailer config so fetchWithFallback absorbs the failure
  3. Verify P0_BASE_URL resolves to the intended environment and API version
  4. Confirm the timeout budget (opts.timeout, sent as floor(ms/1000) seconds) is within what the plan allows

Example fix

// before — single provider, one HTTP failure kills the target
const r = await getProvider('p0').fetch(url);

// after — declare a fallback chain in the retailer acquisition config
const acquisition = { provider: 'p0', fallback: 'playwright', options: { waitForSelector: '.price' } };
const r = await fetchWithFallback(url, acquisition);
Defensive patterns

Strategy: fallback

Validate before calling

// fail fast before the run: provider reachable?
if (!(await getProvider('p0').validate())) { // GET /health
  throw new Error('P0 unavailable — configure acquisition.fallback before scraping');
}

Type guard

function isP0HttpError(err: unknown): boolean {
  return err instanceof Error && /^P0 scrape failed: HTTP \d+$/.test(err.message);
}

Try / catch

try {
  return await p0.fetch(url, opts);
} catch (err) {
  if (isP0HttpError(err)) {
    return await getProvider('playwright').fetch(url, opts); // mirror registry.fetchWithFallback
  }
  throw err;
}

Prevention

When it happens

Trigger: 401/403 with an invalid or revoked P0_API_KEY; 402/429 when the plan is exhausted or rate-limited; 4xx for a scrape body P0 rejects; 5xx during a P0 outage; P0_BASE_URL pointing at the wrong host or path so every call hits a gateway error.

Common situations: P0_API_KEY rotated or expired so every scrape in a retailer run 401s; a new environment pointing P0_BASE_URL at a different stage; rate limits hit because render_js + premium_proxy calls are expensive per request; registry.fetchWithFallback logs '[acquisition] p0 failed ... Falling back to ...' and this error re-throws only when no fallback is configured.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/07c7b91bf137004e. Report an issue: GitHub.