API de PDF com PHP: comprimir com cURL e ler o erro
Como chamar a API de compressão de PDF com PHP e cURL: CURLFile, chave apdf_live_, leitura do status e do Content-Type, e os erros 401, 402 e 429.
Para comprimir um PDF pela API com PHP e cURL, faça um POST multipart para https://api.ajustapdf.com/pdf/compress, com o arquivo no campo file, o nível no campo level e o cabeçalho Authorization com a chave apdf_live_. Quando dá certo, a resposta é o próprio PDF comprimido; quando falha, é um JSON com error e message.
O que a API espera
A compressão é uma operação própria do endpoint /pdf/compress. Ele aceita um arquivo PDF por chamada, no campo file, e o campo level com um dos valores leve, media, forte ou alvo. Sem o campo, o servidor usa o nível médio. Para gerar o PDF no próprio Laravel antes de comprimi-lo, veja gerar PDF no Laravel com DomPDF. Com alvo, envie também targetMb, que aceita valores de 0,05 a 200: é o mesmo mecanismo das páginas de comprimir PDF para 2 MB e de comprimir PDF para 500 KB.
Cada compressão gasta uma operação da cota diária do plano da conta dona da chave. A chave identifica a conta, e não um visitante: por isso a cota que aparece é a sua, do plano que você tem.
A chave de API
A chave é criada na sua conta, em /conta, na seção Chaves de API. Ela começa com apdf_live_ seguido de 32 caracteres, e o site mostra o valor completo só uma vez, no momento da criação. Depois disso, só o prefixo aparece na lista, para você reconhecer a chave. Para revogar, use a mesma seção.
A chamada completa
O arquivo vai num CURLFile, o nível num campo comum e a chave no cabeçalho. Desde o PHP 5.6, o caminho com arroba não é mais aceito por padrão, então o CURLFile é o jeito certo de enviar. O script abaixo comprime um PDF, lê os cabeçalhos de cota e só grava o arquivo quando a resposta é mesmo um PDF:
<?php
$chave = getenv("AJUSTAPDF_KEY");
$cotas = [];
$ch = curl_init("https://api.ajustapdf.com/pdf/compress");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
"file" => new CURLFile("contrato.pdf", "application/pdf", "contrato.pdf"),
"level" => "media",
],
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $chave],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
CURLOPT_HEADERFUNCTION => function ($ch, $linha) use (&$cotas) {
if (stripos($linha, "x-quota-") === 0) {
[$nome, $valor] = explode(":", $linha, 2);
$cotas[strtolower($nome)] = trim($valor);
}
return strlen($linha);
},
]);
$corpo = curl_exec($ch);
if ($corpo === false) {
exit("Falha de rede: " . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$tipo = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
if ($status === 200 && strpos($tipo, "application/pdf") === 0) {
file_put_contents("contrato-comprimido.pdf", $corpo);
echo "Uso hoje: " . ($cotas["x-quota-used"] ?? "?") . " de " . ($cotas["x-quota-limit"] ?? "?");
} else {
$erro = json_decode($corpo, true);
echo $status . " " . ($erro["error"] ?? "ERRO") . ": " . ($erro["message"] ?? "sem mensagem");
}Se a conexão falhar antes de qualquer resposta, curl_exec devolve falso. Trate esse caso à parte, como no script, porque não há status nem corpo para ler.
Ler a resposta sem confundir erro com PDF
Esse é o ponto que mais quebra integrações. Quando o status é 200, o corpo é o PDF e pode ser gravado direto com file_put_contents. Quando não é, o corpo é JSON, com error e message, e gravá-lo num arquivo .pdf produz um arquivo que parece gerado e não abre. Confira sempre o status e o tipo antes de gravar: a resposta de sucesso vem com application/pdf, e a de erro, com application/json, lido em CURLINFO_CONTENT_TYPE.
Uma armadilha comum é ligar CURLOPT_FAILONERROR. Essa opção faz o cURL devolver falso em respostas 4xx e 5xx, mas descarta o corpo, e com ele a mensagem de erro. Se você quer mostrar ao usuário o motivo da falha, deixe a opção desligada e leia o JSON você mesmo.
Cabeçalhos úteis na resposta
| Cabeçalho | Quando aparece | Para que serve |
|---|---|---|
| Content-Type | Sempre | application/pdf no sucesso, application/json no erro |
| x-quota-used | No sucesso | Operações da cota já usadas no dia |
| x-quota-limit | No sucesso | Limite diário do plano |
| x-compress-fitted | Só com o nível alvo | 1 quando coube no tamanho pedido, 0 quando não coube |
| x-compress-level | Só com o nível alvo | O nível usado, ou original quando nada precisou mudar |
Erros que aparecem no PHP
Trate cada código de forma diferente. Repetir a chamada sem mudar nada só faz sentido para o tempo limite. Os demais pedem uma correção antes de tentar de novo.
- 401, INVALID_API_KEY. A chave está revogada ou foi digitada com erro. Gere outra em /conta e atualize a variável de ambiente.
- 402, QUOTA_EXCEEDED. A cota diária da conta acabou. A resposta traz o uso atual e o limite, e a cota volta a contar no dia seguinte.
- 402, FILE_TOO_LARGE. O arquivo passa do limite por arquivo do plano. Comprima antes de enviar, ou use o nível alvo com um tamanho menor.
- 429, TOO_MANY_REQUESTS. Chegaram muitas operações dessa rede. Espere e reduza o ritmo do seu script.
- 400, NO_FILE. O campo file veio vazio ou com outro nome. Confira o CURLFile e o nome do campo.
- 504, TIMEOUT. O processamento passou do tempo permitido. Tente de novo, e, se o arquivo for grande, comprima por partes.
Onde a chave não deve ficar
A chave é segredo de servidor. Se ela aparecer no JavaScript de uma página pública, qualquer visitante a copia e gasta a sua cota. Mantenha a chamada no backend do seu sistema, com a chave em variável de ambiente, e revogue a chave em /conta se ela tiver vazado num repositório.
Onde a API para
Para uma compressão por vez, a chamada acima resolve. Quem trabalha com Java ou C# encontra o mesmo contrato no guia de Java e C#. Para um fluxo em que o próprio usuário envia o arquivo, ele pode usar a ferramenta no site, com a cota dele, e não há necessidade de chave. A API faz sentido quando o seu sistema recebe os PDFs e precisa de um passo automático. Para automações com outras ferramentas, o guia sobre n8n, Make e Zapier mostra o mesmo contrato HTTP em outro ambiente, e o de curl no terminal (ou o de pasta inteira em lote) é o melhor lugar para testar a chave antes de escrever o PHP; quem ainda escolhe como gerar o PDF no próprio sistema compara dompdf, TCPDF e mPDF. Se quem vai chamar as ferramentas é um assistente de IA, o servidor MCP em https://api.ajustapdf.com/mcp aceita a mesma chave.
Perguntas frequentes
Como enviar um PDF para a API de compressão com PHP e cURL?
Crie um cURL apontando para https://api.ajustapdf.com/pdf/compress, envie o arquivo com new CURLFile no campo file e o nível no campo level, e passe o cabeçalho Authorization com Bearer e a sua chave apdf_live_. Leia o status e o Content-Type antes de gravar a resposta como PDF.
Por que o PDF que o PHP salvou da API não abre?
Provavelmente o arquivo recebeu a resposta de erro, que é JSON com os campos error e message. Confira o status HTTP e o Content-Type antes de gravar. Com 401, 402 ou 429, o corpo é texto JSON, e um arquivo com essa mensagem não é um PDF.
Qual chave usar no cabeçalho Authorization da API de PDF?
A chave que começa com apdf_live_, criada em /conta, na seção Chaves de API. Envie no formato Authorization: Bearer seguido da chave, sem aspas. O site mostra a chave completa só na criação, então guarde-a num arquivo de configuração ou numa variável de ambiente do servidor.
Como comprimir um PDF com PHP só até um tamanho máximo?
Envie level com o valor alvo e targetMb com o tamanho em megabytes, de 0,05 a 200. O servidor tenta leve, média e forte, nessa ordem, e entrega o primeiro que caiba. Se nem o forte couber, o cabeçalho x-compress-fitted volta com 0 e o arquivo devolvido é o menor que saiu.