Skip to content

Calling tools

A typed client in TypeScript, Python or Go, and the most common tool calls.

Every tool is POST /v1/tools/<name> with the tool’s input as a JSON body. The answer is the tool’s result as JSON. This page sets up a small client and walks through the most common calls; the endpoint reference has every tool.

Set up a client

The TypeScript, Python and Go examples share one small client file: save it in the folder where you keep your scripts. It sends the token with every request, encodes and decodes JSON, and turns error answers into an ApiError (in Go, a *Problem error) carrying the problem details. Its types follow openapi.json. curl needs no setup.

superpdf.ts
// A typed client for the superPDF API. Types follow openapi.json.
const BASE_URL = 'http://127.0.0.1:7341';
const TOKEN = process.env.SUPERPDF_API_TOKEN;
if (!TOKEN) throw new Error('Set SUPERPDF_API_TOKEN to your API token.');

/** A new PDF written by a tool. */
export interface NewFile {
  output: string;
  pages: number;
  bytes: number;
}

export interface Tool {
  name: string;
  title: string;
  description: string;
  reports_progress: boolean;
  annotations: Record<string, boolean>;
}

/** A heading in the outline, with the pages it covers. */
export interface Section {
  id: string;
  title: string;
  level: number;
  startPage: number;
  endPage: number;
  children: Section[];
}

export interface Structure {
  format: string;
  total_pages: number;
  has_text: boolean;
  truncated: boolean;
  content: string;
  sections: Section[];
}

export interface BatchResult {
  outputs: (NewFile & { input: string })[];
  failed: { input: string; error: string }[];
}

/** RFC 9457 problem details: the body of every error answer. */
export interface Problem {
  type: string;
  title: string;
  status: number;
  detail: string;
  instance: string;
  code: string;
  request_id: string;
  errors?: { field: string; message: string }[];
}

export interface Job<T = unknown> {
  id: string;
  tool: string;
  status: 'queued' | 'running' | 'succeeded' | 'failed';
  progress: { progress: number; total: number; message?: string } | null;
  created_at: string;
  started_at: string | null;
  finished_at: string | null;
  result?: T;
  error?: Problem;
  callback?: {
    url: string;
    status: 'pending' | 'delivering' | 'delivered' | 'failed';
    attempts: number;
    last_status: number | null;
  };
}

/** Thrown for every answer that isn't 2xx. */
export class ApiError extends Error {
  readonly problem: Problem;
  constructor(problem: Problem) {
    super(`${problem.status} ${problem.code}: ${problem.detail}`);
    this.problem = problem;
  }
}

export async function request<T>(
  method: 'GET' | 'POST',
  path: string,
  body?: object,
): Promise<T> {
  const response = await fetch(BASE_URL + path, {
    method,
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: body && JSON.stringify(body),
  });
  const data = await response.json();
  if (!response.ok) throw new ApiError(data);
  return data;
}

export function callTool<T>(name: string, input: object): Promise<T> {
  return request<T>('POST', `/v1/tools/${name}`, input);
}

The examples read the token from SUPERPDF_API_TOKEN; start the API as in Quickstart. Run each example from the folder with the client file:

# Node.js 22.18 or later runs TypeScript directly (Bun and Deno do too)
node list-tools.ts

List tools

GET /v1/tools lists every tool with its title, description and MCP annotations. Tools with readOnlyHint only read; the others write new files.

curl http://127.0.0.1:7341/v1/tools \
  -H "Authorization: Bearer $SUPERPDF_API_TOKEN"
Response
{
  "tools": [
    {
      "name": "pdf_list_files",
      "title": "List PDFs",
      "description": "List PDF files in an allowed folder (not recursive), with siz…",
      "reports_progress": false,
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    …
  ]
}

Call a tool

The body has the same fields as the MCP tool. Relative paths are inside the first allowed folder, and results give absolute paths. This copies pages 1 to 3 of report.pdf into a new file:

curl http://127.0.0.1:7341/v1/tools/pdf_extract_pages \
  -H "Authorization: Bearer $SUPERPDF_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": "report.pdf", "pages": "1-3", "output": "summary.pdf" }'
Response
{
  "output": "/Users/<you>/Documents/superPDF/summary.pdf",
  "pages": 3,
  "bytes": 1547
}

Outputs are always new files. If summary.pdf already exists, the API answers 409 and writes nothing.

Merge PDF

pdf_merge combines 2 or more PDFs, with no upper limit, in the order you list them:

curl http://127.0.0.1:7341/v1/tools/pdf_merge \
  -H "Authorization: Bearer $SUPERPDF_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "inputs": ["report.pdf", "appendix.pdf"], "output": "combined.pdf" }'
Response
{
  "output": "/Users/<you>/Documents/superPDF/combined.pdf",
  "pages": 5,
  "bytes": 2093
}

Convert to Markdown

pdf_extract_structure returns a document’s text as Markdown, HTML or JSON, plus sections: its outline, from bookmarks or detected headings, with the pages each section covers. The examples save the Markdown as report.md and print the outline:

curl -s http://127.0.0.1:7341/v1/tools/pdf_extract_structure \
  -H "Authorization: Bearer $SUPERPDF_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": "report.pdf", "format": "markdown" }' \
  | jq -r .content > report.md
Response
{
  "format": "markdown",
  "total_pages": 4,
  "has_text": true,
  "truncated": false,
  "content": "# Quarterly report\n\n## Summary\n\nRevenue grew 12% on the same quar…",
  "sections": [
    {
      "title": "Quarterly report",
      "level": 1,
      "startPage": 1,
      "id": "n1",
      "endPage": 4,
      "children": [
        {
          "title": "Summary",
          "level": 2,
          "startPage": 1,
          "id": "n2",
          "endPage": 2,
          "children": []
        },
        {
          "title": "Revenue",
          "level": 2,
          "startPage": 2,
          "id": "n3",
          "endPage": 3,
          "children": []
        },
        {
          "title": "Outlook",
          "level": 2,
          "startPage": 3,
          "id": "n4",
          "endPage": 4,
          "children": []
        },
        {
          "title": "Appendix",
          "level": 2,
          "startPage": 4,
          "id": "n5",
          "endPage": 4,
          "children": []
        }
      ]
    }
  ]
}

Text read from a PDF is whatever the document says. If you pass it to an AI model, treat it as untrusted input, not as instructions.

Handle errors

Error answers are problem details with a stable code and, for input problems, the fields to fix. Here, report.pdf has only 4 pages:

curl -i http://127.0.0.1:7341/v1/tools/pdf_extract_pages \
  -H "Authorization: Bearer $SUPERPDF_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": "report.pdf", "pages": "2-9", "output": "excerpt.pdf" }'
Response
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "urn:superpdf:problem:validation",
  "title": "Invalid input",
  "status": 422,
  "detail": "Invalid input. pages: Page ranges must be between 1 and 4.",
  "instance": "urn:superpdf:request:b20a5176-152e-4b22-9438-6a5e2bc969bd",
  "code": "validation",
  "request_id": "b20a5176-152e-4b22-9438-6a5e2bc969bd",
  "errors": [
    {
      "field": "pages",
      "message": "Page ranges must be between 1 and 4."
    }
  ]
}

Every code is described in Errors.

Long work

A tool call answers when the tool has finished; the client files wait up to 5 minutes. For bigger work, such as a batch of 50 PDFs, run the same call as a background job instead of holding a request open.