heron.tools
OpenAPI 3.1

Heron Tools API Documentation

The Heron Tools API provides a bounded, stateless set of text, conversion, image, and PDF tools. Production base URL:

https://api.heron.tools

The current version is published under the /v1 path. The machine-readable OpenAPI 3.1 contract is the definitive reference for endpoint parameters and response schemas:

https://api.heron.tools/v1/openapi.json

Quick Start

All endpoints except the health check and OpenAPI contract require a Bearer API Key:

export HERON_API_KEY='your-api-key'

curl 'https://api.heron.tools/v1/text/slug' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"text":"Hello Heron Tools"}'

Response:

{
  "data": {
    "slug": "hello-heron-tools",
    "inputLength": 17,
    "slugLength": 17,
    "wordCount": 3
  },
  "meta": {
    "requestId": "5a8d83c3-4d9e-43a7-8d94-3f9356881269"
  }
}

Production API Keys are not currently available through a self-service endpoint and must be issued by the Heron Tools API operator. Do not embed long-lived API Keys in public web pages or client packages; publicly accessible browser code cannot keep secrets. Server-side requests are not subject to browser CORS restrictions.

General Conventions

Authentication

Endpoints that require authentication must include:

Authorization: Bearer <HERON_API_KEY>

Missing or invalid credentials return 401 Unauthorized with WWW-Authenticate: Bearer realm="heron-tools-api".

Only these endpoints do not require authentication:

  • GET /v1/health
  • GET /v1/openapi.json

Request ID

Every response includes X-Request-Id. The meta.requestId in successful JSON responses and the requestId in error responses contain the same value. Record this value when troubleshooting.

Successful JSON Responses

JSON endpoints use this standard response format:

{
  "data": {},
  "meta": {
    "requestId": "..."
  }
}

The JSON request body limit is 16 KiB.

Error Responses

Errors use application/problem+json:

{
  "type": "https://api.heron.tools/problems/invalid-input",
  "title": "Invalid input",
  "status": 422,
  "code": "INVALID_INPUT",
  "detail": "The request did not match the endpoint contract.",
  "requestId": "5a8d83c3-4d9e-43a7-8d94-3f9356881269",
  "issues": [
    {
      "path": "text",
      "message": "Too big: expected string to have <=10000 characters"
    }
  ]
}

issues appears only when field-level validation errors exist. Common status codes:

Status CodeMeaning
400Request body is missing or cannot be read
401API Key is missing or invalid
404Route does not exist
413Request body exceeds the endpoint limit
415Unsupported Content-Type
422Invalid parameter, file, or operation
500Internal API or deployment configuration error
502Upstream exchange-rate service is unavailable

Clients should branch on the HTTP status code and stable code field rather than parsing the detail text.

File Requests and Privacy

  • Image and single-PDF endpoints accept file bytes directly in the request body, not JSON or Base64.
  • Single-file requests may include X-Filename for metadata or the download filename; the maximum length is 255 characters.
  • The image request body limit is 20 MiB.
  • The total PDF request body limit, including the complete multipart request for Merge, is 12 MiB.
  • Files are used only to generate the current response and are not written to persistent storage.
  • Binary download responses include Cache-Control: no-store and Content-Disposition: attachment.

Endpoint Overview

MethodPathAuthenticationOutput
GET/v1/healthNoJSON
GET/v1/openapi.jsonNoOpenAPI JSON
POST/v1/text/slugYesJSON
POST/v1/conversions/unitYesJSON
GET/v1/exchange-rates/latestYesJSON
POST/v1/images/infoYesJSON
POST/v1/images/transformYesImage
POST/v1/pdfs/infoYesJSON
POST/v1/pdfs/mergeYesPDF
POST/v1/pdfs/extractYesPDF
POST/v1/pdfs/deleteYesPDF
POST/v1/pdfs/rotateYesPDF
POST/v1/pdfs/splitYesZIP
POST/v1/pdfs/compressYesPDF

System Endpoints

Health Check

GET /v1/health
curl 'https://api.heron.tools/v1/health'
{
  "data": {
    "status": "ok",
    "service": "heron-tools-api",
    "version": "v1"
  },
  "meta": {
    "requestId": "..."
  }
}

OpenAPI Contract

GET /v1/openapi.json
curl 'https://api.heron.tools/v1/openapi.json' --output heron-tools-openapi.json

You can import this file into client generators, API testing tools, or documentation viewers that support OpenAPI 3.1.

Text Endpoints

Generate a Slug

POST /v1/text/slug
Content-Type: application/json

Request fields:

FieldTypeRequiredDefaultDescription
textstringYesInput text, up to 10,000 characters
delimiterstringNo-Delimiter, up to 8 characters; an empty or alphanumeric value falls back to -
lowercasebooleanNotrueWhether to convert the output to lowercase
dedupeDelimiterbooleanNotrueWhether to merge consecutive delimiters
curl 'https://api.heron.tools/v1/text/slug' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{
    "text": "Heron Tools: Fast & Private",
    "delimiter": "_",
    "lowercase": true,
    "dedupeDelimiter": true
  }'

The data object in a successful response contains slug, inputLength, slugLength, and wordCount.

Conversion Endpoints

Unit Conversion

POST /v1/conversions/unit
Content-Type: application/json

Request fields:

FieldTypeRequiredDescription
valuenumberYesFinite numeric value
fromstringYesSource unit ID
tostringYesTarget unit ID; must be in the same category as the source unit
curl 'https://api.heron.tools/v1/conversions/unit' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"value":1,"from":"mile","to":"kilometer"}'

The data object in a successful response contains input, output, formattedOutput, summary, from, and to.

Supported unit IDs:

CategoryUnit IDs
Lengthmeter, kilometer, centimeter, millimeter, inch, foot, yard, mile
Weightgram, kilogram, milligram, ounce, pound, stone
Temperaturecelsius, fahrenheit, kelvin
Volumeliter, milliliter, cubic-meter, teaspoon, tablespoon, fluid-ounce, cup, pint, quart, gallon

Combining units from different categories returns 422 and INCOMPATIBLE_UNITS.

Latest Exchange Rate

GET /v1/exchange-rates/latest?base=USD&quote=CNY

base and quote are both required three-letter currency codes and are case-insensitive. Data comes from Frankfurter; the upstream request timeout is 5 seconds.

curl 'https://api.heron.tools/v1/exchange-rates/latest?base=USD&quote=CNY' \
  -H "Authorization: Bearer $HERON_API_KEY"

The data object in a successful response contains date, base, quote, and rate. This is the latest available exchange rate for the currency pair and is not guaranteed to be a real-time trading quote.

Image Endpoints

Supported input Content-Type values:

  • image/avif
  • image/gif
  • image/heic
  • image/jpeg
  • image/png
  • image/svg+xml
  • image/webp

Images are processed by Cloudflare Images within the current request. The input limit is 20 MiB.

Read Image Information

POST /v1/images/info
Content-Type: <actual image type>
curl 'https://api.heron.tools/v1/images/info' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: image/jpeg' \
  -H 'X-Filename: photo.jpg' \
  --data-binary @photo.jpg

The data object in a successful response contains format and, when detectable, fileSize, width, and height.

Transform an Image

POST /v1/images/transform?<parameters>
Content-Type: <actual image type>

Query parameters:

ParameterType/Allowed ValuesDefaultDescription
widthinteger 1..12000Output width
heightinteger 1..12000Output height
fitscale-down, contain, pad, squeeze, cover, cropSizing mode; pad, cover, and crop require both width and height
gravityauto, center, entropy, face, left, right, top, bottomCrop focal area
backgroundhexadecimal CSS colorFill color, such as %23ffffff; # must be encoded in the URL
rotate0, 90, 180, 270Clockwise rotation angle
fliph, v, hvHorizontal, vertical, or both
formatavif, jpeg, png, webpwebpOutput format
qualityinteger 1..10085Output quality
curl 'https://api.heron.tools/v1/images/transform?width=1200&format=webp&quality=82' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: image/jpeg' \
  -H 'X-Filename: photo.jpg' \
  --data-binary @photo.jpg \
  --output photo-transformed.webp

The output type is determined by format. The response filename appends -transformed to the original base name.

PDF Endpoints

Except for Merge, PDF endpoints use a raw request body:

Content-Type: application/pdf
X-Filename: document.pdf

The total limit per request is 12 MiB. Page numbering starts at 1; ranges use the format 1-3,5,8, cannot be in descending order, and cannot exceed the document’s page count.

Binary PDF responses may include these headers:

Response HeaderMeaning
X-Page-CountNumber of pages in the output PDF; for Split, the total pages across all output files
X-File-CountNumber of PDFs generated by Split
X-Metadata-ClearedWhether Compress cleared the metadata
X-Original-SizeCompress input size in bytes
X-Output-SizeCompress output size in bytes
X-Savings-BytesBytes saved by Compress; may be negative

Read PDF Information

POST /v1/pdfs/info
curl 'https://api.heron.tools/v1/pdfs/info' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf

The data object in a successful response contains:

  • Always present: fileName, fileSize, pageCount, encrypted
  • Optional: title, author, subject, keywords, creator, producer, creationDate, modificationDate

Date fields are ISO 8601 strings.

Merge PDFs

POST /v1/pdfs/merge
Content-Type: multipart/form-data

Use repeated files fields to merge 2 to 10 PDFs in upload order. The complete multipart request cannot exceed 12 MiB.

curl 'https://api.heron.tools/v1/pdfs/merge' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -F 'files=@first.pdf;type=application/pdf' \
  -F 'files=@second.pdf;type=application/pdf' \
  --output merged.pdf

Extract Pages

POST /v1/pdfs/extract?pages=1-3,5

Creates a new PDF in the order specified by pages.

curl 'https://api.heron.tools/v1/pdfs/extract?pages=1-3,5' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf \
  --output document-extracted.pdf

Delete Pages

POST /v1/pdfs/delete?pages=2,4-6

pages specifies the pages to delete. At least one page must remain.

curl 'https://api.heron.tools/v1/pdfs/delete?pages=2,4-6' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf \
  --output document-pages-removed.pdf

Rotate Pages

POST /v1/pdfs/rotate?angle=90&pages=1-3,5
ParameterRequiredDescription
angleYes90, 180, or 270; rotates clockwise from each page’s current angle
pagesNoPages to rotate; omit to rotate all pages
curl 'https://api.heron.tools/v1/pdfs/rotate?angle=90&pages=1-3,5' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf \
  --output document-rotated.pdf

Split a PDF

POST /v1/pdfs/split?ranges=1-2;3-5;6

Separate output file groups with semicolons. Within each group, commas can combine page numbers and ranges. For example, 1,3-4;5-8 generates two PDFs. The response is a ZIP file containing one PDF for each group.

Semicolons have special meaning in the shell, so place the URL in quotes:

curl 'https://api.heron.tools/v1/pdfs/split?ranges=1-2;3-5;6' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf \
  --output document-split.zip

Losslessly Repack a PDF

POST /v1/pdfs/compress?clearMetadata=false

clearMetadata is optional and accepts true or false; the default is false.

curl 'https://api.heron.tools/v1/pdfs/compress?clearMetadata=true' \
  -H "Authorization: Bearer $HERON_API_KEY" \
  -H 'Content-Type: application/pdf' \
  -H 'X-Filename: document.pdf' \
  --data-binary @document.pdf \
  --output document-compressed.pdf

This endpoint rewrites the PDF using object streams and can clear document metadata. It does not rasterize pages or reduce image quality; output may not be smaller for PDFs that are already optimized. For rendering, lossy compression of scanned documents, or PDF-to-image conversion, use the browser-based Heron Tools instead of this API.

Local Development

The project requires Node.js 22.x. After installing dependencies, create a local secrets file from the example:

npm install
cp .dev.vars.example .dev.vars

Replace HERON_API_KEY in .dev.vars with a random local key. This file is ignored by Git; do not commit it. Start the Worker:

npm run api:dev

The default local URL is http://localhost:8787:

curl 'http://localhost:8787/v1/text/slug' \
  -H 'Authorization: Bearer replace-with-your-local-key' \
  -H 'Content-Type: application/json' \
  --data '{"text":"Hello Heron Tools"}'

Validate the API:

npm run api:test
npm run api:check

api:check generates Worker types, runs TypeScript checks, checks test types, and completes a Wrangler dry-run build.

Deployment Notes

On the first deployment, the Worker does not yet exist, so Wrangler cannot bind the required secret in advance. Validate first, then complete the initial deployment using the Git-ignored .dev.vars file:

npm run api:check
npx wrangler deploy --config wrangler.api.jsonc --secrets-file .dev.vars

After the Worker has been created, use these commands to set or rotate the production key:

npx wrangler secret put HERON_API_KEY --config wrangler.api.jsonc
npm run api:deploy

The production Worker, static site, and deployment workflows are independent. Deploying the website does not automatically deploy the API.