# Fusionner des documents PDF

Combinez plusieurs documents PDF en un seul, dans l’ordre où vous les envoyez.

Combinez plusieurs documents PDF en un seul. Les fichiers sont fusionnés
exactement dans l’ordre où ils apparaissent dans la requête : vous contrôlez
donc la séquence finale des pages. Envoyez autant de fichiers que nécessaire en
un seul appel. L’API est *stateless* : votre document est traité dans la région
et n’est jamais stocké.

## Endpoint

<Endpoint method="POST" path="/v1/merge_documents" />

Disponible dans toutes les régions. Consultez [Régions et résidence des
données](/docs/api/regions-and-data-residency) pour le routage et la résidence
des données.

| Région           | URL                                                  |
| ---------------- | ---------------------------------------------------- |
| Global           | `https://api.pdfblocks.com/v1/merge_documents`       |
| États-Unis       | `https://us.api.pdfblocks.com/v1/merge_documents`    |
| HIPAA États-Unis | `https://hipaa.api.pdfblocks.com/v1/merge_documents` |
| Union européenne | `https://eu.api.pdfblocks.com/v1/merge_documents`    |
| Royaume-Uni      | `https://uk.api.pdfblocks.com/v1/merge_documents`    |
| Canada           | `https://ca.api.pdfblocks.com/v1/merge_documents`    |
| Australie        | `https://au.api.pdfblocks.com/v1/merge_documents`    |
| Japon            | `https://jp.api.pdfblocks.com/v1/merge_documents`    |
| Inde             | `https://in.api.pdfblocks.com/v1/merge_documents`    |
| Brésil           | `https://br.api.pdfblocks.com/v1/merge_documents`    |

## Authentification

Authentifiez chaque requête avec votre clé d’API secrète dans l’en-tête
`X-API-Key`, via HTTPS. Créez et gérez vos clés depuis le
[dashboard](https://dashboard.pdfblocks.com). Consultez
[Authentification](/docs/api/authentication) pour plus de détails.

## Requête

L’endpoint accepte un corps de requête `multipart/form-data`.

<ParamField name="file" type="file[]" required>
  Les documents PDF d’entrée, envoyés sous forme de parties `file` répétées.
  Fournissez-en au moins une ; envoyez autant de fichiers que nécessaire en une
  seule requête. Les documents sont fusionnés exactement dans l’ordre où les
  parties apparaissent dans la requête. Consultez [Travailler avec les
  fichiers](/docs/api/working-with-files) pour savoir comment envoyer plusieurs
  parties `file`.
</ParamField>

## Exemples

Fusionnez trois PDF en un seul, dans l’ordre :

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/merge_documents \
  -H 'X-API-Key: your_api_key' \
  -F file=@chapter-1.pdf \
  -F file=@chapter-2.pdf \
  -F file=@chapter-3.pdf \
  -o merged.pdf
```

```python title="Python"
# pip install requests
import requests

files = [
    ('file', open('chapter-1.pdf', 'rb')),
    ('file', open('chapter-2.pdf', 'rb')),
    ('file', open('chapter-3.pdf', 'rb')),
]

response = requests.post(
    'https://api.pdfblocks.com/v1/merge_documents',
    headers={'X-API-Key': 'your_api_key'},
    files=files,
)

response.raise_for_status()
with open('merged.pdf', 'wb') as output:
    output.write(response.content)
```

```javascript title="Node.js"
// Node.js 18+
import { readFile, writeFile } from 'node:fs/promises';

const body = new FormData();
body.append('file', new Blob([await readFile('chapter-1.pdf')]), 'chapter-1.pdf');
body.append('file', new Blob([await readFile('chapter-2.pdf')]), 'chapter-2.pdf');
body.append('file', new Blob([await readFile('chapter-3.pdf')]), 'chapter-3.pdf');

const response = await fetch('https://api.pdfblocks.com/v1/merge_documents', {
  method: 'POST',
  headers: { 'X-API-Key': 'your_api_key' },
  body,
});

if (!response.ok) throw new Error(`Request failed: ${response.status}`);
await writeFile('merged.pdf', Buffer.from(await response.arrayBuffer()));
```

```php title="PHP"
<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';

use GuzzleHttp\Client;

// Repeat the `file` part once per document: they merge in the order sent.
$response = (new Client())->post('https://api.pdfblocks.com/v1/merge_documents', [
    'headers' => ['X-API-Key' => 'your_api_key'],
    'multipart' => [
        ['name' => 'file', 'contents' => fopen('chapter-1.pdf', 'r'), 'filename' => 'chapter-1.pdf'],
        ['name' => 'file', 'contents' => fopen('chapter-2.pdf', 'r'), 'filename' => 'chapter-2.pdf'],
        ['name' => 'file', 'contents' => fopen('chapter-3.pdf', 'r'), 'filename' => 'chapter-3.pdf'],
    ],
]);

if ($response->getStatusCode() === 200) {
    file_put_contents('merged.pdf', $response->getBody());
}
```

```ruby title="Ruby"
# gem install http
require 'http'

response = HTTP
  .headers('X-API-Key' => 'your_api_key')
  .post('https://api.pdfblocks.com/v1/merge_documents', form: {
    file: [
      HTTP::FormData::File.new('chapter-1.pdf'),
      HTTP::FormData::File.new('chapter-2.pdf'),
      HTTP::FormData::File.new('chapter-3.pdf'),
    ],
  })

File.write('merged.pdf', response.body) if response.status.success?
```

```go title="Go"
package main

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var buf bytes.Buffer
	form := multipart.NewWriter(&buf)

	for _, name := range []string{"chapter-1.pdf", "chapter-2.pdf", "chapter-3.pdf"} {
		file, _ := os.Open(name)
		part, _ := form.CreateFormFile("file", name)
		io.Copy(part, file)
		file.Close()
	}
	form.Close()

	req, _ := http.NewRequest("POST", "https://api.pdfblocks.com/v1/merge_documents", &buf)
	req.Header.Set("Content-Type", form.FormDataContentType())
	req.Header.Set("X-API-Key", "your_api_key")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	out, _ := os.Create("merged.pdf")
	defer out.Close()
	io.Copy(out, res.Body)
}
```

```csharp title="C#"
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");

using var form = new MultipartFormDataContent
{
    { new ByteArrayContent(File.ReadAllBytes("chapter-1.pdf")), "file", "chapter-1.pdf" },
    { new ByteArrayContent(File.ReadAllBytes("chapter-2.pdf")), "file", "chapter-2.pdf" },
    { new ByteArrayContent(File.ReadAllBytes("chapter-3.pdf")), "file", "chapter-3.pdf" },
};

var response = await client.PostAsync(
    "https://api.pdfblocks.com/v1/merge_documents", form);
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync(
    "merged.pdf", await response.Content.ReadAsByteArrayAsync());
```

</CodeGroup>

## Réponse

En cas de succès, la réponse est `200 OK` et le PDF fusionné constitue le corps :

```http
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 96124
```

La sortie est un seul PDF dont le nombre de pages est la somme de ceux des
documents d’entrée ; ses pages sont disposées dans l’ordre de la requête.
Écrivez le corps directement dans un fichier, comme le font les exemples ci-dessus ; rien n’est
stocké de notre côté.

## Erreurs

Les requêtes en échec renvoient un corps `application/problem+json`. L’erreur la
plus fréquente sur cet endpoint est un `400`, renvoyé lorsque l’une des parties
`file` n’est pas un PDF lisible. L’objet `errors` nomme le champ :

```json
{
  "type": "https://www.pdfblocks.com/docs/api/v1/error/400",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "file": ["Could not parse the PDF document. The file may be invalid or corrupt."]
  }
}
```

Une `X-API-Key` absente ou non valide renvoie un `401`. Consultez
[Erreurs](/docs/api/errors) pour tous les codes de statut et la forme complète
de la réponse.

## Recettes

Variantes courantes. Dépliez-en une pour la voir dans tous les langages.

<AccordionGroup>

<Accordion title="Placer une page de couverture devant un rapport">

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/merge_documents \
  -H 'X-API-Key: your_api_key' \
  -F file=@cover.pdf \
  -F file=@report.pdf \
  -o report-with-cover.pdf
```

```python title="Python"
import requests

files = [
    ('file', open('cover.pdf', 'rb')),
    ('file', open('report.pdf', 'rb')),
]

response = requests.post(
    'https://api.pdfblocks.com/v1/merge_documents',
    headers={'X-API-Key': 'your_api_key'},
    files=files,
)

response.raise_for_status()
with open('report-with-cover.pdf', 'wb') as output:
    output.write(response.content)
```

```javascript title="Node.js"
import { readFile, writeFile } from 'node:fs/promises';

const body = new FormData();
body.append('file', new Blob([await readFile('cover.pdf')]), 'cover.pdf');
body.append('file', new Blob([await readFile('report.pdf')]), 'report.pdf');

const response = await fetch('https://api.pdfblocks.com/v1/merge_documents', {
  method: 'POST',
  headers: { 'X-API-Key': 'your_api_key' },
  body,
});

if (!response.ok) throw new Error(`Request failed: ${response.status}`);
await writeFile('report-with-cover.pdf', Buffer.from(await response.arrayBuffer()));
```

```php title="PHP"
<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$response = (new Client())->post('https://api.pdfblocks.com/v1/merge_documents', [
    'headers' => ['X-API-Key' => 'your_api_key'],
    'multipart' => [
        ['name' => 'file', 'contents' => fopen('cover.pdf', 'r'), 'filename' => 'cover.pdf'],
        ['name' => 'file', 'contents' => fopen('report.pdf', 'r'), 'filename' => 'report.pdf'],
    ],
]);

if ($response->getStatusCode() === 200) {
    file_put_contents('report-with-cover.pdf', $response->getBody());
}
```

```ruby title="Ruby"
require 'http'

response = HTTP
  .headers('X-API-Key' => 'your_api_key')
  .post('https://api.pdfblocks.com/v1/merge_documents', form: {
    file: [
      HTTP::FormData::File.new('cover.pdf'),
      HTTP::FormData::File.new('report.pdf'),
    ],
  })

File.write('report-with-cover.pdf', response.body) if response.status.success?
```

```go title="Go"
package main

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var buf bytes.Buffer
	form := multipart.NewWriter(&buf)

	for _, name := range []string{"cover.pdf", "report.pdf"} {
		file, _ := os.Open(name)
		part, _ := form.CreateFormFile("file", name)
		io.Copy(part, file)
		file.Close()
	}
	form.Close()

	req, _ := http.NewRequest("POST", "https://api.pdfblocks.com/v1/merge_documents", &buf)
	req.Header.Set("Content-Type", form.FormDataContentType())
	req.Header.Set("X-API-Key", "your_api_key")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	out, _ := os.Create("report-with-cover.pdf")
	defer out.Close()
	io.Copy(out, res.Body)
}
```

```csharp title="C#"
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");

using var form = new MultipartFormDataContent
{
    { new ByteArrayContent(File.ReadAllBytes("cover.pdf")), "file", "cover.pdf" },
    { new ByteArrayContent(File.ReadAllBytes("report.pdf")), "file", "report.pdf" },
};

var response = await client.PostAsync(
    "https://api.pdfblocks.com/v1/merge_documents", form);
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync(
    "report-with-cover.pdf", await response.Content.ReadAsByteArrayAsync());
```

</CodeGroup>

</Accordion>

</AccordionGroup>

## Actions associées

<CardGroup cols={2}>

<Card title="Extraire des pages" href="/docs/api/extract-pages-from-pdf">
  Récupérez un sous-ensemble de pages du fichier fusionné.
</Card>

<Card title="Réorganiser les pages" href="/docs/api/reorder-pages-of-pdf">
  Réagencez les pages après la fusion.
</Card>

<Card title="Diviser à une page" href="/docs/api/split-pdf-at-page">
  Séparez à nouveau le document combiné.
</Card>

<Card title="Ajouter un filigrane texte" href="/docs/api/add-text-watermark-to-pdf">
  Apposez un filigrane sur le document fusionné.
</Card>

</CardGroup>
