Guia 2 de 8
Converter um arquivo: enviar, acompanhar e baixar o resultado
Toda ferramenta de arquivo funciona igual: você envia, a API cria uma tarefa, você espera terminar e baixa. Este guia usa o Comprimir PDF, mas vale para todas.
O que você vai construir
Um script que envia um PDF para POST /v1/tools/comprimir-pdf, acompanha a tarefa até o fim e salva o arquivo comprimido. Em cURL, Node.js, Python e PHP.
Antes de começar
- Uma chave de API em
CONVERTAJA_KEY(veja Primeiros passos). - Um PDF de teste chamado
contrato.pdfna pasta onde você vai rodar os exemplos.
Passo 1: Descubra as opções da ferramenta
Cada ferramenta tem um id (ex.: comprimir-pdf) e opções próprias. Consulte com GET /v1/tools/{id} (grátis):
curl "https://convertaja.com.br/v1/tools/comprimir-pdf" \
-H "Authorization: Bearer $CONVERTAJA_KEY"
{
"id": "comprimir-pdf",
"operation": "pdf-compress",
"name": "Comprimir PDF",
"accepted_extensions": [
"pdf"
],
"multiple": true,
"min_files": 1,
"max_files": null,
"options_schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"level": {
"title": "Compressão",
"type": "string",
"enum": [
"low",
"medium",
"high"
],
"x-enum-labels": {
"low": "Leve",
"medium": "Recomendada",
"high": "Máxima"
},
"default": "medium"
}
}
},
"heavy": false,
"kind": "light",
"credits_per_file": 1,
"docs_url": "https://convertaja.com.br/api/docs/comprimir-pdf"
}Opções que não existem ou valores fora da lista são recusados com 400 invalid_option antes de cobrar qualquer crédito.
Passo 2: Envie o arquivo e crie a tarefa
Mande o arquivo em multipart/form-data no campo files (repita o campo para vários arquivos). As opções vão no campo options como JSON, ou como campos soltos (-F "level=high"):
curl -i -X POST "https://convertaja.com.br/v1/tools/comprimir-pdf" \
-H "Authorization: Bearer $CONVERTAJA_KEY" \
-F "files=@contrato.pdf" \
-F 'options={"level":"high"}'import { openAsBlob } from 'node:fs';
const form = new FormData();
form.append('files', await openAsBlob('contrato.pdf'), 'contrato.pdf');
form.append('options', JSON.stringify({ level: 'high' }));
const res = await fetch('https://convertaja.com.br/v1/tools/comprimir-pdf', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.CONVERTAJA_KEY}` },
body: form,
});
const job = await res.json(); // 202: tarefa aceita e em andamento
console.log(res.status, job.id, job.status);import json
import os
import requests
with open("contrato.pdf", "rb") as f:
res = requests.post(
"https://convertaja.com.br/v1/tools/comprimir-pdf",
headers={"Authorization": f"Bearer {os.environ['CONVERTAJA_KEY']}"},
files=[("files", ("contrato.pdf", f, "application/pdf"))],
data={"options": json.dumps({"level": "high"})},
timeout=120,
)
job = res.json() # 202: tarefa aceita e em andamento
print(res.status_code, job["id"], job["status"])<?php
$ch = curl_init('https://convertaja.com.br/v1/tools/comprimir-pdf');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CONVERTAJA_KEY')],
CURLOPT_POSTFIELDS => [
'files' => new CURLFile('contrato.pdf', 'application/pdf'),
'options' => json_encode(['level' => 'high']),
],
]);
$job = json_decode(curl_exec($ch), true); // 202: tarefa aceita e em andamento
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . " {$job['id']} {$job['status']}\n";
A resposta chega na hora, com status 202: a tarefa foi aceita e os créditos foram reservados. O cabeçalho Location aponta para a tarefa.
{
"id": "AxLIGjFJHd93",
"tool": "comprimir-pdf",
"operation": "pdf-compress",
"status": "running",
"progress": 0,
"message": "Processando…",
"created_at": "2026-10-16T20:23:36.199Z",
"finished_at": null,
"expires_at": "2026-10-16T21:23:36.199Z",
"credits_charged": 1,
"credits_refunded": 0,
"test": false,
"outputs": []
}Passo 3: Acompanhe até terminar
Consulte GET /v1/jobs/{id} a cada 1 ou 2 segundos até status virar done ou error. A consulta é grátis, mas conta no limite por minuto.
# Troque AxLIGjFJHd93 pelo id da sua tarefa
curl "https://convertaja.com.br/v1/jobs/AxLIGjFJHd93" \
-H "Authorization: Bearer $CONVERTAJA_KEY"
{
"id": "Fi_TgsjgpM19",
"tool": "comprimir-pdf",
"operation": "pdf-compress",
"status": "done",
"progress": 100,
"message": "Pronto!",
"created_at": "2026-10-16T20:36:06.144Z",
"finished_at": "2026-10-16T20:36:07.523Z",
"expires_at": "2026-10-16T21:36:06.144Z",
"credits_charged": 1,
"credits_refunded": 0,
"test": false,
"outputs": [
{
"index": 0,
"name": "contrato - comprimido.pdf",
"size": 34031,
"url": "https://convertaja.com.br/v1/jobs/Fi_TgsjgpM19/files/0"
}
],
"download_url": "https://convertaja.com.br/v1/jobs/Fi_TgsjgpM19/download",
"info": "5.5 MB → 33 KB (99% menor)"
}info traz um resumo da ferramenta (aqui, o tamanho antes e depois). expires_at é quando os arquivos são apagados: 1 hora depois da criação.
Dica Prefere não ficar consultando? Envie um webhook_url e receba um aviso quando a tarefa terminar. Veja o guia Webhooks.
Passo 4: Baixe o resultado
Cada arquivo gerado está em outputs[].url. download_url baixa tudo: o próprio arquivo quando é um só, ou um ZIP quando são vários. As duas URLs exigem a mesma chave:
# Um arquivo pela posição (outputs[].url)…
curl -f -o contrato-comprimido.pdf "https://convertaja.com.br/v1/jobs/AxLIGjFJHd93/files/0" \
-H "Authorization: Bearer $CONVERTAJA_KEY"
# …ou tudo de uma vez (download_url): o próprio arquivo, ou um ZIP se houver vários
curl -f -OJ "https://convertaja.com.br/v1/jobs/AxLIGjFJHd93/download" \
-H "Authorization: Bearer $CONVERTAJA_KEY"
O -f faz o cURL falhar em vez de salvar a mensagem de erro (JSON) dentro do “PDF”.
Passo 5: Junte tudo num script
Os três passos juntos, com tratamento de erro. Troque comprimir-pdf e as opções por qualquer outra ferramenta:
# Requer curl e jq. 1) Envia e espera até 60 s pelo resultado
curl -s -X POST "https://convertaja.com.br/v1/tools/comprimir-pdf?wait=true" \
-H "Authorization: Bearer $CONVERTAJA_KEY" \
-F "files=@contrato.pdf" \
-F 'options={"level":"high"}' > tarefa.json
# 2) Confere o status e baixa o resultado
if [ "$(jq -r .status tarefa.json)" = "done" ]; then
curl -fs -o contrato-comprimido.pdf "$(jq -r .download_url tarefa.json)" \
-H "Authorization: Bearer $CONVERTAJA_KEY"
jq -r .info tarefa.json
else
jq . tarefa.json
fi// converter.mjs: envia um PDF, acompanha a tarefa e baixa o resultado
import { openAsBlob } from 'node:fs';
import { writeFile } from 'node:fs/promises';
const BASE = 'https://convertaja.com.br/v1';
const headers = { Authorization: `Bearer ${process.env.CONVERTAJA_KEY}` };
async function api(path, options = {}) {
const res = await fetch(BASE + path, { ...options, headers });
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
return data;
}
// 1. Envia o arquivo (a resposta chega na hora, com status 202)
const form = new FormData();
form.append('files', await openAsBlob('contrato.pdf'), 'contrato.pdf');
form.append('options', JSON.stringify({ level: 'high' }));
let job = await api('/tools/comprimir-pdf', { method: 'POST', body: form });
console.log(`Tarefa ${job.id} criada (${job.credits_charged} crédito)`);
// 2. Consulta a cada 1,5 s até terminar
while (job.status !== 'done' && job.status !== 'error') {
await new Promise((r) => setTimeout(r, 1500));
job = await api(`/jobs/${job.id}`);
}
if (job.status === 'error') {
throw new Error(`A tarefa falhou: ${job.error.message} (créditos devolvidos: ${job.credits_refunded})`);
}
// 3. Baixa cada arquivo gerado (a URL também exige a chave)
for (const out of job.outputs) {
const res = await fetch(out.url, { headers });
await writeFile(out.name, Buffer.from(await res.arrayBuffer()));
console.log(`Salvo: ${out.name} (${out.size} bytes)`);
}
console.log(job.info);# converter.py: envia um PDF, acompanha a tarefa e baixa o resultado
import json
import os
import time
import requests
BASE = "https://convertaja.com.br/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['CONVERTAJA_KEY']}"}
def api(method, path, **kwargs):
res = requests.request(method, BASE + path, headers=HEADERS, timeout=120, **kwargs)
data = res.json()
if not res.ok:
raise RuntimeError(f"{res.status_code} {data['error']['code']}: {data['error']['message']}")
return data
# 1. Envia o arquivo (a resposta chega na hora, com status 202)
with open("contrato.pdf", "rb") as f:
job = api("POST", "/tools/comprimir-pdf",
files=[("files", ("contrato.pdf", f, "application/pdf"))],
data={"options": json.dumps({"level": "high"})})
print(f"Tarefa {job['id']} criada ({job['credits_charged']} crédito)")
# 2. Consulta a cada 1,5 s até terminar
while job["status"] not in ("done", "error"):
time.sleep(1.5)
job = api("GET", f"/jobs/{job['id']}")
if job["status"] == "error":
raise RuntimeError(f"A tarefa falhou: {job['error']['message']} (créditos devolvidos: {job['credits_refunded']})")
# 3. Baixa cada arquivo gerado (a URL também exige a chave)
for out in job["outputs"]:
res = requests.get(out["url"], headers=HEADERS, timeout=120)
res.raise_for_status()
with open(out["name"], "wb") as f:
f.write(res.content)
print(f"Salvo: {out['name']} ({out['size']} bytes)")
print(job.get("info"))<?php
// converter.php: envia um PDF, acompanha a tarefa e baixa o resultado
const BASE = 'https://convertaja.com.br/v1';
function api(string $method, string $path, $body = null): array {
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CONVERTAJA_KEY')],
]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) throw new RuntimeException("$status {$data['error']['code']}: {$data['error']['message']}");
return $data;
}
// 1. Envia o arquivo (a resposta chega na hora, com status 202)
$job = api('POST', '/tools/comprimir-pdf', [
'files' => new CURLFile('contrato.pdf', 'application/pdf'),
'options' => json_encode(['level' => 'high']),
]);
echo "Tarefa {$job['id']} criada ({$job['credits_charged']} crédito)\n";
// 2. Consulta a cada 1,5 s até terminar
while (!in_array($job['status'], ['done', 'error'], true)) {
usleep(1500000);
$job = api('GET', "/jobs/{$job['id']}");
}
if ($job['status'] === 'error') {
throw new RuntimeException("A tarefa falhou: {$job['error']['message']} (créditos devolvidos: {$job['credits_refunded']})");
}
// 3. Baixa cada arquivo gerado (a URL também exige a chave)
foreach ($job['outputs'] as $out) {
$fp = fopen($out['name'], 'wb');
$ch = curl_init($out['url']);
curl_setopt_array($ch, [
CURLOPT_FILE => $fp,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CONVERTAJA_KEY')],
]);
curl_exec($ch);
fclose($fp);
echo "Salvo: {$out['name']} ({$out['size']} bytes)\n";
}
echo $job['info'] . "\n";
Tarefa Fi_TgsjgpM19 criada (1 crédito)
Salvo: contrato - comprimido.pdf (34031 bytes)
5.5 MB → 33 KB (99% menor)Atalho: esperar na mesma chamada com ?wait=true
Para arquivos pequenos e ferramentas rápidas, acrescente ?wait=true (espera até 60 s) ou ?wait=20 (até 20 s). Se terminar a tempo, a resposta é 200 já com outputs; se não, é o mesmo 202 de antes e você continua consultando. O exemplo em cURL do passo 5 usa esse atalho.
Atenção Não use wait com timeout curto no seu cliente HTTP: se a sua conexão cair, a tarefa continua rodando e já foi cobrada. Para tarefas pesadas (OCR, Office, vídeo), prefira 202 + consulta ou webhook.
Enviar por URL ou base64 (JSON)
Se o arquivo já está num endereço público, mande só a URL; baixamos com limite de tamanho do plano e de tempo (60 s). Endereços de rede interna são bloqueados (422 forbidden_url).
curl -X POST "https://convertaja.com.br/v1/tools/comprimir-pdf?wait=30" \
-H "Authorization: Bearer $CONVERTAJA_KEY" \
-H "Content-Type: application/json" \
-d '{"files":[{"url":"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"}],"options":{"level":"high"}}'const res = await fetch('https://convertaja.com.br/v1/tools/comprimir-pdf?wait=30', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.CONVERTAJA_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
files: [{ url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf' }],
options: { level: 'high' },
}),
});
const job = await res.json();
console.log(res.status, job.status, job.outputs?.map((o) => o.url));import os
import requests
res = requests.post(
"https://convertaja.com.br/v1/tools/comprimir-pdf",
params={"wait": "30"},
headers={"Authorization": f"Bearer {os.environ['CONVERTAJA_KEY']}"},
json={
"files": [{"url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"}],
"options": {"level": "high"},
},
timeout=90,
)
job = res.json()
print(res.status_code, job["status"], [o["url"] for o in job.get("outputs", [])])<?php
$ch = curl_init('https://convertaja.com.br/v1/tools/comprimir-pdf?wait=30');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CONVERTAJA_KEY'), 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'files' => [['url' => 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf']],
'options' => ['level' => 'high'],
]),
]);
$job = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . " {$job['status']}\n";
Ou mande o conteúdo em base64 (corpo JSON de até 60 MB; acima disso, use multipart ou URL):
import { readFile } from 'node:fs/promises';
const res = await fetch('https://convertaja.com.br/v1/tools/comprimir-pdf?wait=true', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.CONVERTAJA_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
files: [{ base64: (await readFile('contrato.pdf')).toString('base64'), name: 'contrato.pdf' }],
options: { level: 'high' },
}),
});
console.log(await res.json());import base64
import os
import requests
with open("contrato.pdf", "rb") as f:
conteudo = base64.b64encode(f.read()).decode()
res = requests.post(
"https://convertaja.com.br/v1/tools/comprimir-pdf",
params={"wait": "true"},
headers={"Authorization": f"Bearer {os.environ['CONVERTAJA_KEY']}"},
json={"files": [{"base64": conteudo, "name": "contrato.pdf"}], "options": {"level": "high"}},
timeout=90,
)
print(res.json())<?php
$ch = curl_init('https://convertaja.com.br/v1/tools/comprimir-pdf?wait=true');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CONVERTAJA_KEY'), 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'files' => [['base64' => base64_encode(file_get_contents('contrato.pdf')), 'name' => 'contrato.pdf']],
'options' => ['level' => 'high'],
]),
]);
print_r(json_decode(curl_exec($ch), true));
Erros e o que fazer
Erros de validação voltam na hora, antes de cobrar:
| HTTP | code | Quando |
|---|---|---|
| 400 | missing_files | Nenhum arquivo no campo files. |
| 400 | invalid_option | Opção que não existe ou valor fora da lista. |
| 400 | too_many_files | Mais arquivos do que a ferramenta ou o plano permitem. |
| 402 | insufficient_credits | Saldo menor que o custo da chamada. |
| 404 | tool_not_found / job_not_found | Id errado, ou a tarefa já passou de 1 hora. |
| 413 | file_too_large | Arquivo maior que o limite do plano. |
| 415 | unsupported_file_type | Extensão que a ferramenta não aceita. |
| 422 | forbidden_url | URL de arquivo aponta para rede interna. |
| 429 | concurrency_limit | Tarefas pesadas demais ao mesmo tempo. Espere uma terminar. |
{
"error": {
"code": "invalid_option",
"message": "Valor inválido para \"level\". Use um destes: low, medium, high.",
"request_id": "req_UkbrW3QRi59d"
}
}{
"error": {
"code": "unsupported_file_type",
"message": "Tipo de arquivo não aceito por esta ferramenta: \"nota.xml\". Aceitos: pdf.",
"request_id": "req_UkbrW3QRi59d"
}
}{
"error": {
"code": "insufficient_credits",
"message": "Créditos insuficientes: esta chamada custa 2 e você tem 1. Compre um pacote ou mude de plano em https://convertaja.com.br/conta/api.",
"credits_required": 2,
"credits_remaining": 1,
"request_id": "req_UkbrW3QRi59d"
}
}Se o problema estiver no conteúdo do arquivo, a tarefa é aceita (202) e termina com status: "error". Nesse caso os créditos voltam sozinhos (credits_refunded):
{
"id": "lrzqWwqfYv4a",
"tool": "xml-nfe-para-pdf",
"operation": "nfe-danfe",
"status": "error",
"progress": 0,
"message": "Nenhuma NF-e encontrada. Envie os arquivos XML das notas (ou um ZIP com eles). (1 arquivo ignorado por não ser NF-e)",
"created_at": "2026-10-16T20:24:25.824Z",
"finished_at": "2026-10-16T20:24:25.998Z",
"expires_at": "2026-10-16T21:24:25.824Z",
"credits_charged": 1,
"credits_refunded": 1,
"test": false,
"outputs": [],
"error": {
"code": "job_failed",
"message": "Nenhuma NF-e encontrada. Envie os arquivos XML das notas (ou um ZIP com eles). (1 arquivo ignorado por não ser NF-e)"
}
}Próximos passos
Todos os endpoints e opções: referência da API. Dúvidas? Fale com a gente e informe o X-Request-Id da chamada.