Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/formfeed:formfeed-pdf-tools-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Formfeed PDF tools API
version: 1.0.0
description: Generate PDFs and images from templates and JSON data, one API call per document.
termsOfService: https://formfeed.dev/legal/terms
contact:
name: Formfeed support
email: support@formfeed.dev
url: https://formfeed.dev
servers:
- url: https://api-eu.formfeed.dev/v1
description: EU (Frankfurt), where every workspace renders and stores today
- url: https://api.formfeed.dev/v1
description: Routes to the workspace's region
- url: https://api-us.formfeed.dev/v1
description: Reserved for a US region, which is not open yet
security:
- bearerAuth: []
tags:
- name: PDF tools
description: Merge, protect, watermark and inspect PDFs.
paths:
/pdf/merge:
post:
tags:
- PDF tools
operationId: mergePdf
summary: Merge PDFs
description: 'Joins the PDF outputs of earlier renders, in the order given, into a new PDF. The result is a
render of its own (it appears in the render log and `GET /renders/{id}`) and costs 0.5 units on
live keys. Sources are render ids of the same workspace whose output still exists; uploaded
files come with the file library. Part of the Starter plan and above.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- sources
properties:
sources:
type: array
minItems: 2
maxItems: 50
items:
type: string
description: Render id (`rnd_…`) of a succeeded PDF render, or a PDF of the workspace library by id (`fil_…`) or name.
example:
- rnd_7da09ab6cd9946b798c187c2a9350852
- terms.pdf
filename:
type: string
example: bundle.pdf
expires_in:
type:
- integer
- 'null'
access:
type: string
enum:
- public
- signed
meta:
type: object
additionalProperties: true
storage:
allOf:
- $ref: '#/components/schemas/StorageOption'
responses:
'200':
description: Merged render.
content:
application/json:
schema:
$ref: '#/components/schemas/Render'
default:
$ref: '#/components/responses/Problem'
/pdf/protect:
post:
tags:
- PDF tools
operationId: protectPdf
summary: Password-protect a PDF
description: 'Encrypts the PDF output of an earlier render with AES-256 into a new render (0.5 units on live
keys). `user_password` is needed to open the document; without it anyone can open it, but the
permissions still apply. `permissions` lists what readers may do; everything not listed is
forbidden. Protect last: an encrypted PDF cannot be merged or watermarked afterwards.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- source
properties:
source:
type: string
description: Render id (`rnd_…`) of a succeeded PDF render.
user_password:
type: string
maxLength: 127
description: Needed to open the document.
owner_password:
type: string
maxLength: 127
description: Lifts the restrictions. A random one is used when omitted.
permissions:
type: array
items:
type: string
enum:
- print
- copy
- modify
- annotate
default: []
filename:
type: string
expires_in:
type:
- integer
- 'null'
access:
type: string
enum:
- public
- signed
meta:
type: object
additionalProperties: true
storage:
allOf:
- $ref: '#/components/schemas/StorageOption'
responses:
'200':
description: Protected render.
content:
application/json:
schema:
$ref: '#/components/schemas/Render'
default:
$ref: '#/components/responses/Problem'
/pdf/convert:
post:
tags:
- PDF tools
operationId: convertOfficeToPdf
summary: Convert an office document to PDF
description: 'Converts a Word, Excel, PowerPoint, OpenDocument or RTF file into a PDF render (Starter plan and
above). Upload the file as `multipart/form-data`, or name the render of a Word or PowerPoint
template (output `docx` or `pptx`) as `source` in a JSON body. Accepted are DOCX, XLSX, PPTX, ODT, ODS, ODP, DOC, XLS, PPT, RTF and HTML, up to 20 MB;
the type is read from the file''s content, not its name, so a "save as web page" export that
carries a `.doc` name converts as the HTML it is. Macro-enabled and password-protected
documents are refused. The file is converted and not kept; the PDF is stored like any render.
HTML is converted by LibreOffice, not by the render engine, and the converter loads nothing
from the web: images and stylesheets the document links to stay empty. For HTML you control, a
render with an `html` source gives a better result.
Costs follow the PDF rule: one unit per started five pages on live keys. Chain `/pdf/protect`,
`/pdf/watermark` or `/pdf/merge` on the returned render id.
Fonts the document uses but neither embeds nor finds among the converter''s fonts are replaced
by metric-compatible ones (Carlito for Calibri, Caladea for Cambria, Liberation for Arial and
Times New Roman). Embed fonts in Word for an exact result.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- source
properties:
source:
type: string
description: Render id (`rnd_…`) of a succeeded render whose output is `docx` or `pptx`.
page_ranges:
type: string
maxLength: 100
pattern: ^\d+(-\d+)?(,\d+(-\d+)?)*$
example: 1-3,5
landscape:
type: boolean
default: false
single_page_sheets:
type: boolean
default: false
filename:
type: string
expires_in:
type: integer
access:
type: string
enum:
- public
- signed
meta:
type: object
additionalProperties: true
storage:
allOf:
- $ref: '#/components/schemas/StorageOption'
multipart/form-data:
schema:
type: object
required:
- file
properties:
file:
type: string
format: binary
description: The document, up to 20 MB.
page_ranges:
type: string
maxLength: 100
pattern: ^\d+(-\d+)?(,\d+(-\d+)?)*$
example: 1-3,5
description: Pages to convert; all when omitted.
landscape:
type: boolean
default: false
description: Landscape for spreadsheets and documents without their own page setup.
single_page_sheets:
type: boolean
default: false
description: Each spreadsheet sheet on one page.
filename:
type: string
description: Name of the PDF; the uploaded name with `.pdf` when omitted.
expires_in:
type: integer
access:
type: string
enum:
- public
- signed
meta:
type: string
description: A JSON object as text; returned with the render.
storage:
type: string
description: 'The `storage` option as JSON text: `false`, or `{"key": "…"}`.'
responses:
'200':
description: Converted render.
content:
application/json:
schema:
$ref: '#/components/schemas/Render'
'413':
description: File too large.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'415':
description: Not an accepted office document.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'503':
description: The converter is busy; retry after `Retry-After` seconds.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
default:
$ref: '#/components/responses/Problem'
/pdf/watermark:
post:
tags:
- PDF tools
operationId: watermarkPdf
summary: Watermark a PDF
description: 'Draws a text or an image diagonally across every page of the PDF output of an earlier render,
into a new render (0.5 units on live keys). The text is set in Helvetica Bold, which covers
Western European languages; an image is a PNG or JPEG of the workspace file library, scaled to
60 % of the page width. Pass `text` or `image`, not both.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- source
properties:
source:
type: string
description: Render id (`rnd_…`) of a succeeded PDF render.
text:
type: string
maxLength: 200
example: COPY
image:
type: string
maxLength: 200
description: 'Instead of `text`: a PNG or JPEG of the workspace library, by id (`fil_…`) or name.'
opacity:
type: number
minimum: 0
maximum: 1
default: 0.2
rotation:
type: number
default: -45
description: Degrees clockwise as in CSS; the default runs from bottom left to top right.
color:
type: string
pattern: ^#?[0-9a-fA-F]{6}$
example: '#cc0000'
description: Hex colour; grey by default.
filename:
type: string
expires_in:
type:
- integer
- 'null'
access:
type: string
enum:
- public
- signed
meta:
type: object
additionalProperties: true
storage:
allOf:
- $ref: '#/components/schemas/StorageOption'
responses:
'200':
description: Watermarked render.
content:
application/json:
schema:
$ref: '#/components/schemas/Render'
default:
$ref: '#/components/responses/Problem'
/pdf/info:
post:
tags:
- PDF tools
operationId: pdfInfo
summary: Inspect a PDF
description: Page count, page sizes, encryption and metadata of the PDF output of an earlier render. Free on every plan; creates no render.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- source
properties:
source:
type: string
description: Render id (`rnd_…`).
responses:
'200':
description: PDF info.
content:
application/json:
schema:
type: object
properties:
source:
type: string
page_count:
type: integer
pages:
type: array
items:
type: object
properties:
width_pt:
type: number
height_pt:
type: number
width_mm:
type: number
height_mm:
type: number
encrypted:
type: boolean
metadata:
type: object
additionalProperties: true
default:
$ref: '#/components/responses/Problem'
components:
schemas:
Render:
type: object
required:
- id
- status
- output
- region
- created_at
properties:
id:
type: string
example: rnd_01J8ZK4M3Q7X
status:
$ref: '#/components/schemas/RenderStatus'
output:
$ref: '#/components/schemas/OutputFormat'
download_url:
type:
- string
- 'null'
format: uri
expires_at:
type:
- string
- 'null'
format: date-time
bytes:
type:
- integer
- 'null'
page_count:
type:
- integer
- 'null'
units:
type: number
example: 1
region:
$ref: '#/components/schemas/Region'
environment:
type: string
enum:
- live
- test
template:
type:
- object
- 'null'
properties:
id:
type: string
slug:
type: string
version:
type: integer
channel:
type:
- string
- 'null'
description: The release channel the version came from (`published` by default); null when the request named a version number or `latest`.
canary:
type: boolean
description: True when the channel's canary share picked this version.
engine_version:
type: string
description: Chromium build and engine package version, for reproducibility.
example: chromium-140.0.7339.16/engine-1.4.0
template_checksum:
type:
- string
- 'null'
output_sha256:
type:
- string
- 'null'
deduplicated:
type: boolean
storage:
allOf:
- $ref: '#/components/schemas/StorageDelivery'
timings:
type: object
properties:
queue_ms:
type: integer
compile_ms:
type: integer
render_ms:
type: integer
upload_ms:
type: integer
total_ms:
type: integer
error:
allOf:
- $ref: '#/components/schemas/Problem'
meta:
type: object
additionalProperties: true
created_at:
type: string
format: date-time
completed_at:
type:
- string
- 'null'
format: date-time
RenderStatus:
type: string
enum:
- queued
- rendering
- succeeded
- failed
Region:
type: string
enum:
- eu
- us
- apac
OutputFormat:
type: string
enum:
- pdf
- png
- jpg
- webp
- docx
- pptx
description: PDF and image templates, ad-hoc HTML and URLs produce `pdf`, `png`, `jpg` or `webp`; a `docx` template produces `docx` or `pdf`, a `pptx` template `pptx` or `pdf`.
StorageOption:
description: 'The copy for the workspace''s own bucket (bring your own storage). Left out, every output is
copied under the connection''s key template. `false` leaves this document out. `key` names its
object below the connection''s prefix instead of the key template: relative, without `.` or `..`
segments, at most 1024 bytes; an existing object is overwritten. A render with its own key is
never answered from the dedup cache, and one without a connection answers 409
`storage_not_configured`. In a batch, each item carries its own.
'
oneOf:
- type: boolean
enum:
- false
- type: object
additionalProperties: false
properties:
key:
type: string
maxLength: 1024
example: invoices/2026/RE-1001.pdf
StorageDelivery:
type: object
description: 'The output''s copy in the workspace''s own bucket; null when none applies (no connection, a
paused one, `storage: false`, a test render the connection does not take). A synchronous
response reports `pending`: the copy is made in the background, and `storage.delivered` or
`storage.failed` follow as webhook events.
'
properties:
status:
type: string
enum:
- pending
- delivered
- failed
bucket:
type: string
example: fennlor-docs
key:
type: string
example: formfeed/2026/09/rnd_7da09ab6cd9946b798c187c2a9350852/invoice.pdf
url:
type:
- string
- 'null'
description: The object below the connection's `public_base_url`; null without one.
reason:
type:
- string
- 'null'
enum:
- permanent
- rejected
- exhausted
- source_gone
- connection_deleted
- connection_paused
- plan
- workspace_deleted
description: 'Why a delivery failed: the credentials or bucket (`permanent`), the object (`rejected`), retries used up (`exhausted`), the output expired first (`source_gone`), or the connection went away.'
error:
allOf:
- $ref: '#/components/schemas/StorageError'
StorageError:
type: object
description: 'The provider''s answer, reduced: its code, the HTTP status and the first line of its message.'
properties:
code:
type: string
example: AccessDenied
http_status:
type:
- integer
- 'null'
example: 403
message:
type: string
example: Access Denied
Problem:
type: object
required:
- type
- title
- status
properties:
type:
type: string
format: uri
example: https://docs.formfeed.dev/errors/template-syntax
title:
type: string
example: Template syntax error
status:
type: integer
example: 422
detail:
type: string
code:
type: string
enum:
- template_not_found
- template_syntax_error
- template_runtime_error
- data_validation_error
- navigation_failed
- render_timeout
- page_limit_exceeded
- output_too_large
- file_too_large
- source_not_found
- source_invalid
- source_encrypted
- tool_failed
- file_type_unsupported
- office_document_invalid
- office_document_too_large
- office_conversion_failed
- office_unavailable
- unsupported_in_office
- test_output_unavailable
- quota_exceeded
- rate_limited
- region_unavailable
- worker_exhausted
- invalid_request
- unauthorized
- forbidden
- not_found
- conflict
- not_implemented
- schema_breaking_change
- channel_not_found
- channel_name_invalid
- channel_in_use
- render_input_expired
- listen_session_limit
- listen_session_ended
- last_workspace
- storage_not_configured
- storage_test_failed
location:
type:
- object
- 'null'
properties:
line:
type: integer
column:
type: integer
file:
type: string
enum:
- template
- css
- head
- header
- footer
part:
type: string
example: word/document.xml
description: 'Office templates: the part of the file (document, header, footer, slide).'
paragraph:
type: integer
description: 'Office templates: the paragraph within the part, from 1.'
request_id:
type: string
errors:
type: array
description: '`data_validation_error` only: every place where the data does not match the schema.'
items:
type: object
properties:
path:
type:
- string
- 'null'
example: data.invoice.number
message:
type: string
example: 'data.invoice.number: must be string'
index:
type: integer
description: 'Batch requests only: the first item that failed.'
responses:
Problem:
description: Error (RFC 9457 problem details).
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: API key
description: 'API key from the workspace: `ff_live_…` or `ff_test_…`. Also accepted in the `X-API-Key` header.'
externalDocs:
description: Guides, SDKs and the CLI
url: https://docs.formfeed.dev