India API

Docs › Files, documents and media

PDF tools API

Merge, split, rotate and compress PDFs, read their text and draw their pages.

Upload a PDF and get a new one back: merged with others, cut down to some pages, rotated, or made smaller. Or read its details and text, or draw a page as a picture. Password-protected PDFs are refused. Files are processed and deleted; nothing is kept or logged.

Base URL https://api.dharun.devNo API key600 requests a minuteThis page as Markdown

Pages, size and details of a PDF

POST/v1/pdf/info

Page count, PDF version, title, author, dates, page sizes (named when A4, Letter and so on), and whether it has forms, signatures or attachments.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

What you send here is never written to the log, and the answer is not cached.

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" "https://api.dharun.dev/v1/pdf/info"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/info", { method: "POST", body: form });
const body = await response.json();
if (!response.ok) throw new Error(body.error.message);
console.log(body.data);

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/info", files=files, timeout=90)
body = response.json()
if not response.ok:
    raise RuntimeError(body["error"]["message"])
print(body["data"])

Response

{
  "data": {
    "pages": 2,
    "version": "1.7",
    "title": "India API sample document",
    "author": "India API",
    "subject": "",
    "creator": "Typst 0.15.1",
    "producer": "",
    "created": "D:20261015000000+00'00'",
    "modified": "D:20261015000000+00'00'",
    "keywords": [],
    "page_sizes": [
      {
        "width": 595.28,
        "height": 841.89,
        "name": "A4"
      }
    ],
    "encrypted": false,
    "form": false,
    "signatures": false,
    "tagged": true,
    "linearized": false,
    "bookmarks": true,
    "attachments": 0,
    "bytes": 35632
  },
  "meta": {
    "note": "Page sizes are in points (1/72 inch), each different size listed once."
  }
}

Text of a PDF, page by page

POST/v1/pdf/text

The text of each page, in reading order, or laid out as on the page with layout=true. needs_ocr is true when the pages hold almost no text, as in a scan: send those to /v1/ocr. Options go in the query string, or as form fields when the file is sent as multipart/form-data.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

What you send here is never written to the log, and the answer is not cached.

ParameterDescription
firstqueryFirst page. Default 1.
lastqueryLast page; at most 500 pages per request.
layoutqueryKeep the page's layout with spaces, for tables. One of: true, false.

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" "https://api.dharun.dev/v1/pdf/text"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/text", { method: "POST", body: form });
const body = await response.json();
if (!response.ok) throw new Error(body.error.message);
console.log(body.data);

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/text", files=files, timeout=90)
body = response.json()
if not response.ok:
    raise RuntimeError(body["error"]["message"])
print(body["data"])

Response, with lists and long text shortened

{
  "data": {
    "pages": [
      {
        "page": 1,
        "text": "India API sample document\n\nThis two-page PDF is a sample for the PDF tools of India API. Send it to the endpoints to\nread its details and text, take its pages a…"
      },
      {
        "page": 2,
        "text": "Page two\n\nவணக்கம். இது இந்தியா API-இன் மாதிரி ஆவணம்.\nनमस्ते। यह इंडिया API का नमूना दस्तावेज़ है।\nThank you for trying it."
      }
    ],
    "characters": 460,
    "needs_ocr": false
  },
  "meta": {
    "total": 2
  }
}

Merge PDFs into one

POST/v1/pdf/merge

Send two to twenty PDFs as form fields, in order; up to 25 MB together. Returns one PDF.

Send: Two to twenty PDFs as multipart/form-data, up to 25 MB together. Post the file as the request body, or as multipart/form-data with one field per file. Try it with this sample.

The answer returns the PDF itself; errors are JSON as everywhere else.

What you send here is never written to the log, and the answer is not cached.

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" -F "[email protected]" -o out.pdf "https://api.dharun.dev/v1/pdf/merge"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
form.append("file", input.files[1]);
const response = await fetch("https://api.dharun.dev/v1/pdf/merge", { method: "POST", body: form });
if (!response.ok) throw new Error((await response.json()).error.message);
const file = await response.blob();

Python

import requests

files = [("file", open("document-1.pdf", "rb")), ("file", open("document-2.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/merge", files=files, timeout=90)
if not response.ok:
    raise RuntimeError(response.json()["error"]["message"])
open("out.pdf", "wb").write(response.content)

Pick, reorder or split pages

POST/v1/pdf/pages

Returns a PDF of the pages named, in the order named; a page may be named twice. To split a PDF, ask for each part in turn. Options go in the query string, or as form fields when the file is sent as multipart/form-data.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

The answer returns the PDF itself; errors are JSON as everywhere else.

What you send here is never written to the log, and the answer is not cached.

ParameterDescription
pagesquery, requiredPages: numbers and ranges such as 1-3,5,8- (8 to the end), odd, even or l (the last page).

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" -o out.pdf "https://api.dharun.dev/v1/pdf/pages?pages=2,1"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/pages?pages=2,1", { method: "POST", body: form });
if (!response.ok) throw new Error((await response.json()).error.message);
const file = await response.blob();

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/pages?pages=2,1", files=files, timeout=90)
if not response.ok:
    raise RuntimeError(response.json()["error"]["message"])
open("out.pdf", "wb").write(response.content)

Rotate pages

POST/v1/pdf/rotate

Turns pages clockwise by a multiple of 90 degrees. Options go in the query string, or as form fields when the file is sent as multipart/form-data.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

The answer returns the PDF itself; errors are JSON as everywhere else.

What you send here is never written to the log, and the answer is not cached.

ParameterDescription
anglequeryDegrees, clockwise. Default 90. One of: 90, 180, 270, -90, -180, -270.
pagesqueryPages: numbers and ranges such as 1-3,5,8- (8 to the end), odd, even or l (the last page).

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" -o out.pdf "https://api.dharun.dev/v1/pdf/rotate?angle=90&pages=1"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/rotate?angle=90&pages=1", { method: "POST", body: form });
if (!response.ok) throw new Error((await response.json()).error.message);
const file = await response.blob();

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/rotate?angle=90&pages=1", files=files, timeout=90)
if not response.ok:
    raise RuntimeError(response.json()["error"]["message"])
open("out.pdf", "wb").write(response.content)

Make a PDF smaller

POST/v1/pdf/compress

Removes duplicate fonts and images and packs the file's structure. Pictures are not re-compressed, so a scanned PDF shrinks little. The original size is in the X-Original-Bytes header; when nothing was gained, the original comes back.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

The answer returns the PDF itself; errors are JSON as everywhere else.

What you send here is never written to the log, and the answer is not cached.

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" -o out.pdf "https://api.dharun.dev/v1/pdf/compress"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/compress", { method: "POST", body: form });
if (!response.ok) throw new Error((await response.json()).error.message);
const file = await response.blob();

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/compress", files=files, timeout=90)
if not response.ok:
    raise RuntimeError(response.json()["error"]["message"])
open("out.pdf", "wb").write(response.content)

Draw a page as a picture

POST/v1/pdf/render

Returns one page as PNG or JPEG, for previews and thumbnails. Options go in the query string, or as form fields when the file is sent as multipart/form-data.

Send: A PDF, up to 10 MB. Post the file as the request body, or as multipart/form-data. Try it with this sample.

The answer returns the image itself; errors are JSON as everywhere else.

What you send here is never written to the log, and the answer is not cached.

ParameterDescription
pagequeryPage number. Default 1.
widthqueryWidth in pixels, 50 to 3000. Default 1000.
formatqueryDefault png. One of: png, jpeg.

curl

# a sample to try: https://api.dharun.dev/samples/document.pdf
curl -F "[email protected]" -o out.png "https://api.dharun.dev/v1/pdf/render?page=1&width=600"

JavaScript

// input is an <input type="file"> element
const form = new FormData();
form.append("file", input.files[0]);
const response = await fetch("https://api.dharun.dev/v1/pdf/render?page=1&width=600", { method: "POST", body: form });
if (!response.ok) throw new Error((await response.json()).error.message);
const file = await response.blob();

Python

import requests

files = [("file", open("document.pdf", "rb"))]
response = requests.post("https://api.dharun.dev/v1/pdf/render?page=1&width=600", files=files, timeout=90)
if not response.ok:
    raise RuntimeError(response.json()["error"]["message"])
open("out.png", "wb").write(response.content)

Using an AI assistant? Connect the MCP server and it can call this API by itself.