ScreenshotNeo

BlogHTML to image & PDF

PDFShift URL Conversion Times Out: Causes and Fixes

Diagnose PDFShift 408 responses and client-side timeouts, then reduce slow resources and wait for page content safely.

By the ScreenshotNeo team4 October 202610 min read

When a PDFShift URL conversion times out, first identify which layer timed out. An HTTP 408 JSON response means PDFShift says its conversion exceeded the available time. A local timeout or abort with no PDFShift response usually means your HTTP client or an intermediary stopped waiting first. PDFShift documents a default overall conversion timeout of 30 seconds on free plans and 100 seconds on paid plans; the limit includes loading, processing, and PDF generation. PDFShift’s FAQ is the source for these current-as-reviewed limits, so check it before relying on them.

The most useful fixes are to reduce remote resources, avoid a client timeout shorter than the conversion window, and make JavaScript readiness checks specific and bounded. A timeout is not proof that PDFShift is down or that one particular resource failed; establish the response and reproduce the issue before assigning a cause.

1. Identify the timeout layer

What you observe What it indicates What to check
HTTP 408 with a JSON response from PDFShift The conversion service reached its time limit. Source loading, processing, readiness waits, and your account’s timeout limit.
Your client throws a timeout or abort error, with no PDFShift response The caller or an intermediary may have stopped waiting first. HTTP client timeout, proxy deadline, load balancer deadline, and application deadline.
A PDF arrives, but a chart, font, or other dynamic content is missing Page load completion may have happened before that content finished rendering. Add a condition that checks the required content is actually ready.
Images are missing, especially below the fold Lazy loading or failed image requests may be involved. Trigger loading and ensure readiness logic handles failed images.

PDFShift documents 408 for conversion timeouts. Its guidance also identifies network loading as a main driver of longer conversions. The missing-content and lazy-image rows are diagnostic possibilities, not guarantees about a particular failure. PDFShift FAQ · PDFShift guide to improving conversion time

2. Make the client wait long enough to receive the result

Compare your HTTP client’s timeout with the service-side conversion window. A client deadline of 10 seconds can terminate the request even when PDFShift’s conversion window is longer. Set the client timeout to a value that allows the intended conversion to finish, while keeping an application-level upper bound appropriate for your job. Do not remove all bounds: an unbounded request can tie up a worker indefinitely if the network or service does not return.

Python: distinguish HTTP 408 from a client exception

import requests

API_KEY = "YOUR_API_KEY"
URL = "https://example.com/report"

try:
    response = requests.post(
        "https://api.pdfshift.io/v3/convert/pdf",
        auth=("api", API_KEY),
        json={"source": URL},
        timeout=(10, 120),  # connect timeout, then read timeout
    )
    if response.status_code == 408:
        print("PDFShift returned 408: inspect conversion time and page dependencies")
        print(response.text)
    else:
        response.raise_for_status()
        with open("report.pdf", "wb") as pdf_file:
            pdf_file.write(response.content)
except requests.exceptions.Timeout as exc:
    print("The client timed out before receiving a complete response:", exc)
except requests.exceptions.RequestException as exc:
    print("Request failed:", exc)

Use the endpoint, authentication form, and request fields configured for your PDFShift account and API version; see the PDFShift API documentation. The example’s read timeout is deliberately longer than the documented service-side defaults so the caller can receive a service response. Choose limits appropriate to your application.

Node.js: give the request an explicit deadline

const controller = new AbortController();
const timeoutMs = 120_000;
const timer = setTimeout(() => controller.abort(), timeoutMs);

try {
  const response = await fetch('https://api.pdfshift.io/v3/convert/pdf', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${Buffer.from(`api:${process.env.PDFSHIFT_API_KEY}`).toString('base64')}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ source: 'https://example.com/report' }),
    signal: controller.signal
  });

  if (response.status === 408) {
    console.error('PDFShift returned 408:', await response.text());
  } else if (!response.ok) {
    throw new Error(`PDFShift HTTP ${response.status}: ${await response.text()}`);
  } else {
    const pdf = Buffer.from(await response.arrayBuffer());
    await import('node:fs/promises').then(fs => fs.writeFile('report.pdf', pdf));
  }
} catch (error) {
  if (error.name === 'AbortError') {
    console.error('The Node.js caller aborted before receiving the result');
  } else {
    throw error;
  }
} finally {
  clearTimeout(timer);
}

Use your configured PDFShift endpoint and authentication requirements. Keep the client deadline longer than the expected conversion window, but bounded for your service. If a reverse proxy sits between the application and PDFShift, its deadline must also permit the request to complete.

cURL: inspect status and response body

curl --max-time 120 \
  --user "api:YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"source":"https://example.com/report"}' \
  --output report.pdf \
  --write-out '\nHTTP %{http_code}\n' \
  https://api.pdfshift.io/v3/convert/pdf

For diagnosis, note the final HTTP status and inspect the response body when the call fails. Depending on the endpoint’s error response, a failed response written to the PDF output path may not be a PDF; do not treat file existence alone as success. PDFShift’s FAQ describes a JSON response with status 408 when its conversion times out.

3. Reduce page loading and conversion work

Fetching the source URL and its external resources is a major source of conversion time. Remote images, stylesheets, scripts, and fonts make the conversion depend on additional network requests and availability. PDFShift recommends avoiding network requests where practical. Read its conversion-time guidance.

  • Send HTML directly when you can. If your application already has the page markup, submit HTML rather than asking PDFShift to fetch a URL. Confirm the API’s accepted source format and fields in the official documentation.
  • Inline critical assets where appropriate. Inline critical CSS and JavaScript, and embed images when that is suitable for your payload and workflow.
  • Remove work that does not affect the document. Strip analytics, tracking, animations, and scripts that do not contribute to the PDF output.
  • Optimize images. Use appropriately sized and compressed images. Large images increase transfer and processing work.
  • Check every dependency. Review redirects, remote stylesheets, scripts, images, and fonts. A dependency may be slow or unreachable from the conversion environment; without a reproduction or diagnostic evidence, do not assume which request stalled.

These are ways to reduce work and external dependencies, not guaranteed fixes for every URL. Change one category at a time when diagnosing a page so you can see whether the conversion behavior changes.

4. Wait for the content your PDF actually needs

A page can finish loading resources before an asynchronous chart, application component, or font has finished rendering. If the PDF is produced too early, a targeted readiness condition can help. PDFShift supports wait_for with a global function that checks the condition. That wait consumes the same remaining overall conversion budget, so it must be bounded by the service timeout. See PDFShift documentation.

For example, the page can expose a completion flag after it has rendered the report:

// In the page being converted, after the report is ready:
window.reportReady = true;
// In the PDFShift request options, use a page-specific readiness condition:
{
  "source": "https://example.com/report",
  "wait_for": "window.reportReady === true"
}

Check the precise expression format supported by your API version. Prefer a signal tied to the actual content over a long fixed sleep: a fixed delay may waste time on fast pages and still be too short on slow ones. Avoid a condition that can never become true.

5. Handle lazy-loaded and failed images

PDFShift’s guidance notes that it does not usually scroll a page to trigger lazy-loaded images. A readiness check that requires every image to load can also wait forever when even one image request fails. If below-the-fold images matter, arrange for them to load before conversion and use a condition that accounts for failed requests.

A page-side approach is to scroll through the document to trigger lazy loading, then wait until each image either succeeds or errors:

async function loadLazyImages() {
  const images = Array.from(document.images);
  for (const image of images) {
    image.scrollIntoView({ block: 'center' });
    await new Promise(resolve => requestAnimationFrame(resolve));
  }
  return Promise.all(images.map(image => {
    if (image.complete) return Promise.resolve();
    return new Promise(resolve => {
      image.addEventListener('load', resolve, { once: true });
      image.addEventListener('error', resolve, { once: true });
    });
  }));
}

loadLazyImages().then(() => {
  window.reportImagesSettled = true;
});

This is an illustrative page-side pattern; adapt it to the page and PDFShift’s supported execution options. The important property is that image errors count as settled, so one broken URL does not keep the readiness test false indefinitely. Do not add an unbounded wait.

6. Check workload concurrency separately

PDFShift’s FAQ states a default maximum of 50 parallel conversions. If the issue appears only during batch peaks, compare your simultaneous conversions with that limit and ask PDFShift about a custom allowance if your workload needs more. Concurrency is a separate diagnostic: the documented limit alone does not establish that it caused a specific 408. PDFShift FAQ.

For batch systems, use a bounded worker pool, record per-job status, and retry only errors that are safe to retry. Add backoff between retries so a burst of timed-out work does not create an even larger burst. Avoid retrying a known slow source repeatedly without first reducing its work or changing its readiness behavior.

7. Troubleshooting checklist

Symptom Likely cause Action
PDFShift response has status 408 Total conversion exceeded the account’s available time. Reduce resource work, inspect page readiness, and compare conversion needs with the plan limit.
Python raises requests.exceptions.Timeout Client connect or read timeout is too short, or the network did not respond in time. Set explicit connect/read limits that allow the conversion window; inspect intermediaries too.
Node reports AbortError Your AbortController deadline fired. Increase the caller deadline within your application’s bounds and distinguish this from a returned HTTP 408.
cURL exits with a timeout --max-time expired before a complete response. Set a suitable maximum and inspect the status and body on the next run.
PDF is missing charts or generated text Conversion began before asynchronous rendering completed. Use a specific readiness signal through wait_for.
Only some images are absent or conversion waits for images Lazy loading did not trigger, or an image failed while readiness logic waited for success. Trigger image loading and treat both load and error as settled outcomes.
Slow only during batches Peak workload or concurrency may be relevant. Measure simultaneous jobs; PDFShift documents 50 parallel conversions by default and can discuss custom needs.

Capture the request timestamp, endpoint, status, response body, client exception, configured deadlines, and whether the same source succeeds when simplified. Those facts help distinguish a service response from a caller-side abort without guessing at an undocumented server trace.

8. Timeout, reliability, and cost considerations

The documented 30-second free and 100-second paid defaults are conversion limits, not performance guarantees. PDFShift says paid customers can contact the provider about an adjustment; the reviewed documentation does not publish a universal higher timeout value. Confirm current limits and account terms with PDFShift before designing around them.

For reliability, keep caller and intermediary deadlines coordinated, make readiness conditions finite, and record whether each failure is a service response or a local abort. For throughput, control concurrency and retry with backoff. For cost, do not assume that extending a timeout makes an inefficient page cheaper or faster; first reduce unnecessary remote work and choose an account plan appropriate to the workload. The dossier does not establish a conversion price, so check current PDFShift pricing directly before estimating spend.

Or skip the browser setup

If your task is to capture a clean screenshot of the source page rather than produce a PDF, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, and the API documentation lists its options. This does not change PDFShift’s timeout behavior; it is an alternative when a screenshot or PDF capture fits the job.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie and consent banners as a visitor, then removes more than 60 known consent platforms along with newsletter popups and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server includes take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month with no card.

FAQ

Does an HTTP 408 mean my own HTTP client timed out?

No. A returned 408 is PDFShift’s documented conversion-timeout response. A client exception without that response points you to the caller or an intermediary deadline.

Can I set any timeout I want on a paid PDFShift account?

The reviewed documentation says paid customers can ask about an adjustment, but does not publish a universal configurable maximum.

Will removing external resources always fix a timeout?

No. It reduces network work and dependencies, but each page and failure needs its own diagnosis.

Why can a page load successfully but still produce an incomplete PDF?

Asynchronous page code may render charts or other content after ordinary resource loading finishes. Wait for a signal tied to that content.