Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Guzzle cannot split a PDF by itself. Use it to download (or stream) the source document, then use FPDI with FPDF, TCPDF, or tFPDF to import the pages you need into a newly generated PDF. The example below validates the URL and page list, checks the HTTP response, writes to a temporary file, exports pages 1, 3, and 5 when they exist, and removes temporary data even when processing fails.

What each library does

Guzzle is an HTTP client. Its job is requesting the PDF, following the server’s response rules, and exposing the body as a PSR-7 stream. It does not understand PDF page trees or page numbers. FPDI is the PDF-import layer: it reads an existing file page by page and uses each imported page as a template in an FPDF-, TCPDF-, or tFPDF-based output document.

The result is a selective re-creation, not an in-place edit. FPDI creates a completely new document and places the selected page content into it. That distinction matters for signatures, forms, annotations, bookmarks, encryption, and other interactive features.

Install Guzzle, FPDF, and FPDI

From an existing PHP project, install the packages with Composer:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
  • EDIT text, images & designs in PDF documents. ORGANIZE PDFs. Convert PDFs to Word, Excel & ePub.
  • READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.
  • CREATE, COMBINE, SCAN and COMPRESS PDFs
  • FILL forms & Digitally Sign PDFs. PROTECT and Encrypt PDFs
  • LIFETIME License for 1 Windows PC or Laptop. 5GB MobiDrive Cloud Storage Included.
composer require guzzlehttp/guzzle setasign/fpdf setasign/fpdi

The FPDI package shown here uses FPDF. If your project already uses TCPDF or tFPDF, install the corresponding FPDI integration and adjust the class import and output calls to that library’s API.

Require Composer’s autoloader in the script:

require __DIR__ . '/vendor/autoload.php';

A complete, safe export script

This command-line example downloads a PDF, confirms that the server returned a successful PDF response, imports a requested list of 1-based page numbers, and writes the result to selected-pages.pdf. It uses a temporary file rather than keeping the complete source in PHP memory.

<?php
declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use GuzzleHttpClient;
use GuzzleHttpExceptionGuzzleException;
use setasignFpdiFpdi;

$sourceUrl = 'https://example.com/source.pdf';
$outputPath = __DIR__ . '/selected-pages.pdf';
$requestedPages = [1, 3, 5]; // FPDI page numbers are 1-based.

$tmpPath = tempnam(sys_get_temp_dir(), 'pdf_');
if ($tmpPath === false) {
    throw new RuntimeException('Could not create a temporary file.');
}

try {
    // Basic application-level URL policy. Add an allow-list or SSRF protection in production.
    $parts = parse_url($sourceUrl);
    if ($parts === false || !in_array(strtolower($parts['scheme'] ?? ''), ['https', 'http'], true)) {
        throw new InvalidArgumentException('The source URL must use HTTP or HTTPS.');
    }

    $client = new Client([
        'timeout' => 30,
        'connect_timeout' => 10,
        'http_errors' => false,
        'allow_redirects' => ['max' => 5, 'strict' => true],
        'headers' => ['Accept' => 'application/pdf'],
    ]);

    $response = $client->request('GET', $sourceUrl, ['stream' => true]);
    $status = $response->getStatusCode();
    if ($status < 200 || $status >= 300) {
        throw new RuntimeException("Source returned HTTP {$status}.");
    }

    $contentType = strtolower(trim(explode(';', $response->getHeaderLine('Content-Type'))[0]));
    if ($contentType !== 'application/pdf') {
        throw new RuntimeException("Expected application/pdf, received {$contentType}.");
    }

    $body = $response->getBody();
    $destination = fopen($tmpPath, 'wb');
    if ($destination === false) {
        throw new RuntimeException('Could not open the temporary file for writing.');
    }
    try {
        while (!$body->eof()) {
            $chunk = $body->read(1024 * 1024);
            if ($chunk === '') {
                break;
            }
            if (fwrite($destination, $chunk) === false) {
                throw new RuntimeException('Writing the downloaded PDF failed.');
            }
        }
    } finally {
        fclose($destination);
    }

    $pdf = new Fpdi();
    $pageCount = $pdf->setSourceFile($tmpPath);
    if ($pageCount < 1) {
        throw new RuntimeException('The source PDF has no readable pages.');
    }

    $pagesAdded = 0;
    foreach ($requestedPages as $pageNumber) {
        if (!is_int($pageNumber) || $pageNumber < 1 || $pageNumber > $pageCount) {
            // Ignore invalid numbers; alternatively, fail fast for strict workflows.
            continue;
        }

        $template = $pdf->importPage($pageNumber);
        $size = $pdf->getTemplateSize($template);
        $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
        $pdf->useTemplate($template);
        $pagesAdded++;
    }

    if ($pagesAdded === 0) {
        throw new RuntimeException('None of the requested pages exists in the source PDF.');
    }

    // F = save to a local file. Use D or I only when sending through an HTTP endpoint.
    $pdf->Output('F', $outputPath);
    printf("Wrote %d page(s) to %sn", $pagesAdded, $outputPath);
} catch (GuzzleException $e) {
    throw new RuntimeException('The HTTP request failed: ' . $e->getMessage(), 0, $e);
} finally {
    if (is_file($tmpPath)) {
        unlink($tmpPath);
    }
}

Replace $sourceUrl, $outputPath, and $requestedPages for your application. The output preserves each selected page’s width, height, and orientation. A source with pages 1, 3, and 5 therefore produces a three-page output in that order. Duplicate entries are imported twice unless you de-duplicate the array first.

Turning the script into a web endpoint

For an endpoint that returns the generated file, keep the download and import logic the same, then send the PDF after Output('S') returns its bytes:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$bytes = $pdf->Output('S');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="selected-pages.pdf"');
header('Content-Length: ' . strlen($bytes));
echo $bytes;

Do not emit debugging text, notices, or an HTML error page before these headers. For large outputs, write to a controlled temporary destination and stream that file instead of creating a second large in-memory string.

Accepting page ranges and validating input

Never pass a user-provided page list directly to importPage(). Parse a deliberately small grammar, enforce a maximum number of pages, and reject malformed ranges. This helper accepts values such as 1,3,7-9:

function parsePageSpec(string $spec, int $pageCount, int $maxPages = 100): array
{
    $pages = [];
    foreach (preg_split('/s*,s*/', trim($spec)) as $part) {
        if ($part === '') {
            continue;
        }
        if (preg_match('/^(d+)$/', $part, $m)) {
            $start = $end = (int) $m[1];
        } elseif (preg_match('/^(d+)-(d+)$/', $part, $m)) {
            $start = (int) $m[1];
            $end = (int) $m[2];
            if ($end < $start) {
                throw new InvalidArgumentException('Range end must not be less than its start.');
            }
        } else {
            throw new InvalidArgumentException('Use page numbers or ascending ranges, for example 1,3,7-9.');
        }

        for ($page = $start; $page <= $end; $page++) {
            if ($page >= 1 && $page <= $pageCount) {
                $pages[$page] = true; // de-duplicate while retaining numeric keys
            }
            if (count($pages) > $maxPages) {
                throw new InvalidArgumentException('Too many pages requested.');
            }
        }
    }
    return array_keys($pages);
}

Call this after setSourceFile(), when the real page count is known. Decide whether out-of-range numbers should be ignored, as above, or rejected with a client error; strict rejection is usually clearer for an API.

Rank #2
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
  • Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.
  • Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
  • Read & Annotate. Enjoy intuitive reading modes and powerful tools to comment, highlight, and mark up PDFs.
  • Create & Manage PDFs. Create new PDFs, combine multiple files, scan documents, and compress for easy sharing.
  • Fill & Sign Forms. Complete forms and digitally sign documents with secure e-signature tools.

HTTP and security checks that belong in production

  • URL policy: Restrict schemes, hosts, ports, and redirect destinations. A public “download any URL” endpoint can become a server-side request forgery (SSRF) primitive. Resolve DNS and block private or link-local address ranges according to your infrastructure’s policy.
  • Response validation: Check status, content type, and a maximum download size. A server may return an HTML login page with a successful status, and a missing or misleading content type is possible.
  • Timeouts: Set both connection and total-request limits. Treat timeouts and redirects as expected failures, not uncaught fatal errors.
  • Temporary files: Use a directory with restrictive permissions, generate unpredictable names with tempnam(), and delete files in a finally block.
  • Resource limits: Bound the number of imported pages and concurrent jobs. PDF parsing can consume substantial CPU, memory, and disk space even when the HTTP payload is modest.
  • Untrusted PDFs: Run processing with least-privilege credentials and an isolated worker where possible. Do not assume a PDF is harmless merely because it is not executed as PHP.

What is and is not preserved

FPDI imports page appearance into a new writer document. Text and vector content normally remain usable as PDF content, but the operation is not a lossless editor for every source feature. Test representative files if your workflow depends on any of the following:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Requirement Why it needs a test
Digital signatures Re-creating pages changes the document, so an existing signature should not be expected to remain valid.
Interactive forms Field dictionaries and appearance behavior may not survive page-template import as an interactive form.
Annotations and links Visual page content and interactive annotation objects are different PDF structures.
Bookmarks and named destinations These document-level structures are not automatically rebuilt by a page-by-page import.
Encrypted or malformed files Import support depends on the file and the installed FPDI/PDF parser capabilities; failure should be handled as an input error.
Very large documents Disk, memory, and processing time depend on the file and environment; authoritative generic speed or page-count limits are not established.

Local FPDI versus a remote PDF extraction API

Decision factor Local Guzzle + FPDI Remote extraction service
Data privacy Files can remain in your infrastructure, subject to your temporary-file and logging controls. Files leave your infrastructure; review the provider’s retention, region, and security terms.
Control You control validation, retries, page ordering, and output handling. You depend on the service’s API, limits, authentication, and availability.
PDF feature coverage Validate the exact FPDI/parser combination against your files. Coverage varies; confirm support for encryption, malformed files, annotations, and forms.
Operations You operate PHP workers, storage, and scaling. The provider operates parsing infrastructure, usually for a per-use or plan cost.
Latency Includes download and local parsing. Includes upload, remote processing, and result download.

An API such as APDF documents page extraction with a pages range parameter (for example, 1-3). Verify its current authentication, limits, pricing, and retention terms before selecting it; those details can change.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

“Expected application/pdf”

The URL may redirect to a login page, return an error document, or omit the PDF content type. Inspect the final URL and response headers, authenticate the request when appropriate, and verify the first bytes of a known PDF before loosening validation.

HTTP 401, 403, or 404

Guzzle is reporting the origin server’s response. Supply required authorization or cookies, confirm that the URL is valid, and avoid retrying a permanent 401/403 without changing credentials or policy.

Timeout or connection failure

Check DNS, firewall rules, TLS certificates, and the remote server’s responsiveness. Increase the timeout only when the document legitimately takes longer; also retain a connection timeout so unreachable hosts fail quickly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Unable to find PDF trailer” or an import exception

The response may be truncated, not actually a PDF, encrypted, malformed, or outside the parser’s supported feature set. Save the original response for diagnosis, confirm the download completed, and test the file with the FPDI version you deploy.

Output has the wrong orientation or size

Use getTemplateSize() for every imported template and pass its orientation and dimensions to AddPage(). Hard-coding A4 will distort or crop non-A4 source pages.

Rank #3
Scrivar PDF Pro - Organize, Edit, Compress, Convert, Merge, eSign, OCR & 30+ tools | Lifetime License
  • EVERY PDF TOOL UNLOCKED - 30+ tools in one app: edit text and images, convert, merge, split, compress, sign, OCR, redact, watermark, batch process, and more. No feature gates, no upsells, nothing held back.
  • PAY ONCE, OWN FOREVER — A one-time purchase, not a subscription. Other apps runs $240/year — Scrivar is yours for life, with free updates included.
  • UNLIMITED eSIGN, BUILT IN — Send contracts and forms for signature and track every step. Recipients sign in their browser with no account or app needed. Replace DocuSign and save hundreds a year.
  • PC, MAC, AND WEB — Install on any Win 10/11 PC or macOS 11+ Mac (Intel or Apple Silicon), or work in your browser at scrivar.com. Same tools, same account, everywhere you work.
  • OCR + FULL OFFICE CONVERSION — Turn scanned documents into searchable, selectable text, and convert PDFs to and from Word, Excel, and PowerPoint with formatting kept intact.

Blank or incomplete output

Ensure useTemplate() is called after AddPage(), that the template identifier belongs to the current FPDI document, and that the temporary file remains readable until Output() completes.

Memory or disk exhaustion

Stream the HTTP body to disk, cap download size, limit selected pages, and process jobs in a worker with explicit PHP and operating-system resource limits. There is no universal safe page or megabyte threshold; measure with your own representative files.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your actual goal is generating images or PDFs of web pages rather than extracting pages from an existing PDF, ScreenshotNeo provides a single HTTP call. It is not a replacement for FPDI’s PDF-page import, but it avoids maintaining a browser runtime for website captures:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for parameters and response handling. Before capture, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether the request was billed. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan.

Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without entering a card.

Operational checklist

  • Install compatible Composer packages and commit the lock file.
  • Validate URL scheme, host policy, redirects, status, content type, and maximum size.
  • Stream to a protected temporary file and delete it in all exit paths.
  • Use 1-based page numbers and verify them against setSourceFile()‘s count.
  • Preserve each template’s dimensions and orientation.
  • Decide how your API reports skipped or invalid pages.
  • Test signatures, forms, annotations, links, bookmarks, encryption, and malformed inputs if they matter.
  • Log failures without exposing downloaded document contents or credentials.

Frequently Asked Questions

Are FPDI page numbers zero-based?

No. In the documented importPage() workflow, page numbers start at 1.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I export pages without downloading the PDF first?

The PDF must be available to the parser. Guzzle can stream the response directly to a temporary file, which avoids holding the complete source in PHP memory but does not eliminate the download.

Should I use useImportedPage() instead of useTemplate()?

Use the method provided by the FPDI integration and version installed in your project; the example uses the commonly available useTemplate() call.

Is this process suitable for preserving a signed PDF?

Treat the output as a new document. If signature validity or document-level interactive structures are requirements, test a different workflow against the exact files and compliance rules.

Quick Recap

Bestseller No. 1
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.; CREATE, COMBINE, SCAN and COMPRESS PDFs
$99.99
Bestseller No. 2
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.; Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
$99.99

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.