Skip to main content
Use the fetchWithPayment helper configured in the getting-started guides so 402 invoices are resolved automatically.

1. Submit the PDF by URL

const baseUrl = process.env.HORIZON_BASE_URL ?? 'https://api.horizon.new/v1';

const pdfResponse = await fetchWithPayment(`${baseUrl}/extract/pdf`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sourceUrl: 'https://cdn.example.com/handbooks/agent-playbook.pdf',
    sourceName: 'Agent Playbook 2025',
    options: { segmentLength: 1500, language: 'en', ocr: true },
    webhookUrl: 'https://example.com/webhooks/horizon/extraction',
  }),
});

const pdfJob = await pdfResponse.json();
console.log('pdf job', pdfJob.jobId);

Upload instead of linking (optional)

import FormData from 'form-data';
import { createReadStream } from 'node:fs';

const form = new FormData();
form.append('file', createReadStream('./assets/agent-playbook.pdf'));
form.append('sourceName', 'Agent Playbook 2025');

const uploadResponse = await fetchWithPayment(`${baseUrl}/extract/pdf`, {
  method: 'POST',
  headers: form.getHeaders(),
  body: form,
});

const uploadJob = await uploadResponse.json();

2. Handle synchronous completion

if (pdfJob.status === 'completed' && pdfJob.result) {
  console.log('Inline chunks', pdfJob.result.chunks.length);
}

3. Poll the job

const status = await fetchWithPayment(pdfJob.statusUrl).then((res) => res.json());

if (status.state === 'processing') {
  await new Promise((resolve) => setTimeout(resolve, 3000));
}

if (status.state !== 'succeeded') {
  throw new Error(`PDF extraction failed: ${status.error?.code ?? 'unknown'}`);
}

console.log('chunks', status.result.chunks.length);

4. Apply the output

  • Persist normalized chunks to your knowledge store.
  • Store status.result.sources if you need traceability back to page numbers.
  • Drive /search filters by adding your own annotations when you persist the extraction.