logo

PDF Verification API

Integrate PDF authenticity checking into your application with our simple REST API. Detect modifications, analyze metadata, and protect your document workflow.

5-Layer Analysis
Data Isolation Per Client
Test Environment Available

Quick Start Example

bash
# Basic usage - analyze any publicly accessible PDF
curl -X POST https://htpbe.tech/api/v1/analyze \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/document.pdf"}'

Free testing via browser: For individual PDF checks, visit our homepage. API access is for commercial integration only.

Simple, Transparent Pricing

Choose the plan that fits your needs. All plans include full API access and comprehensive documentation.

Free PDF Verification

For individual PDF checks without API access, use our web interface for free with unlimited checks. API access is for commercial integration only.

Private & Isolated Environment

Each API key is a completely isolated environment. Your API checks do not appear in website statistics and are only accessible to you. Results are stored separately and linked to your account for full privacy and data isolation.

Included in All Plans

Full PDF metadata analysis (5 layers)
Risk scoring & detection methods
Unlimited API keys
Test API keys for synthetic data

Starter

For small projects and testing

$15/month
API checks/month:30
API keys:Unlimited
Overage:$0.6/check
Get Started

Cancel anytime

Popular

Growth

For growing businesses

$149/month
API checks/month:350
API keys:Unlimited
Overage:$0.5/check
Get Started

Cancel anytime

Pro

For high-volume applications

$499/month
API checks/month:1,500
API keys:Unlimited
Overage:$0.4/check
Get Started

Cancel anytime

Privacy

Enterprise

On-premise deployment for maximum data privacy

Custom
API checks/month:Unlimited
API keys:Unlimited
On-premise deployment (Docker/Kubernetes)
Your documents never leave your infrastructure
100% data privacy — perfect for GDPR, HIPAA, PCI DSS compliance
Webhook notifications for async result delivery

🔒 Enterprise: On-Premise Deployment

Deploy the HTPBE analyzer in your own infrastructure — documents never leave your network.

Designed for banks, healthcare providers, government agencies, and legal firms with strict data privacy requirements (GDPR, HIPAA, PCI DSS, SOX compliance).

🔒 100% Data Privacy

Documents analyzed entirely within your infrastructure. No files, metadata, or results ever transmitted to external servers.

📋 Compliance Ready

GDPR, HIPAA, PCI DSS, SOX compliant by design. Your legal and security team approves the deployment — we provide the software.

🐳 Easy Deployment

Single Docker container or Kubernetes deployment. Production-ready in under 30 minutes. No file size limits — configure resources as needed.

🔧 Custom Development

Need specific integrations, custom webhook logic, or modifications to match your business processes? We build it for you.

Pricing: Custom pricing based on your requirements. Includes a dedicated account manager, priority support (1-hour response time guaranteed), and regular updates and security patches.

Technical details: View full on-premise deployment documentation →

API Documentation

Simple REST API with comprehensive PDF analysis

Complete API Reference on GitHub

For detailed field-by-field documentation including all possible values, error codes, and comprehensive examples, visit our GitHub documentation:

Quick Reference

MethodEndpointDescription
POST/api/v1/analyzeAnalyze PDF from URL for modifications
GET/api/v1/result/{id}Retrieve previously completed check by ID
GET/api/v1/checksList all checks with filtering and pagination

Base URL: https://htpbe.tech/api/v1
Authentication: All endpoints require Authorization: Bearer YOUR_API_KEY
Monthly Quota: Depends on your plan (Starter: 30/month, Growth: 350/month, Pro: 1,500/month, Enterprise: unlimited)

Analyze PDF Document

POST https://htpbe.tech/api/v1/analyze

Request Headers

http
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Get your API key from the dashboard after signing up. Supports both htpbe_live_... (production) and htpbe_test_... (testing) keys.

Request Body

json
{
  "url": "https://example.com/documents/contract.pdf",
  "original_filename": "contract.pdf"
}

url (required): Public URL to your PDF file. Must be accessible via HTTP/HTTPS.

original_filename (optional): Original filename of the document. Useful when the URL contains a generated or hashed filename (e.g. from Vercel Blob or S3). When provided, this name is stored and returned in results instead of the filename extracted from the URL.

Supported sources: AWS S3 (presigned URLs), Google Cloud Storage, Azure Blob, Dropbox shared links, your own CDN, or any publicly accessible URL.

Limitations: Max 10 MB file size. URL must be publicly accessible without authentication.

Response (201 Created)

json
{
  "id": "3f9c8b7a-2e1d-4c5f-9b8e-7a6d5c4b3a21"
}

Analysis is performed synchronously. The response contains only the check id — call GET /api/v1/result/{id} immediately after to retrieve the full analysis.

With test keys the ID is a deterministic UUID v4 like 00000000-0000-4000-8000-000000000001 — passes UUID format validation but is obviously synthetic.

Two-Step Usage

bash
# Step 1: Submit for analysis
curl -s -X POST https://htpbe.tech/api/v1/analyze \
  -H "Authorization: Bearer htpbe_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/contract.pdf"}'
# → { "id": "3f9c8b7a-2e1d-4c5f-9b8e-7a6d5c4b3a21" }

# Step 2: Retrieve full result
curl https://htpbe.tech/api/v1/result/3f9c8b7a-2e1d-4c5f-9b8e-7a6d5c4b3a21 \
  -H "Authorization: Bearer htpbe_live_..."
# → { "status": "modified", "origin": { ... }, ... }

Retrieve Check Result

GET https://htpbe.tech/api/v1/result/{id}

Description

Retrieve a previously completed PDF analysis by its unique check ID. Returns the full analysis data including metadata, structure, signatures, and findings. Only returns checks that belong to your API client.

Request Headers

http
Authorization: Bearer YOUR_API_KEY

Path Parameters

id (required): Check ID returned from POST /api/v1/analyze (full UUID v4)

Response (200 OK)

json
{
  "id": "506a6b1b-1360-48a2-b389-abb346f85d04",
  "filename": "contract.pdf",
  "check_date": 1736542583,
  "file_size": 245632,
  "algorithm_version": "2.2.1",
  "current_algorithm_version": "2.2.1",
  "status": "modified",
  "origin": { "type": "institutional", "software": null },
  "creation_date": 1704110400,
  "modification_date": 1707840000,
  "creator": "Adobe Acrobat Pro DC",
  "producer": "Adobe PDF Library 15.0",
  "modification_confidence": "certain",
  "date_sequence_valid": true,
  "metadata_completeness_score": 90,
  "xref_count": 2,
  "has_incremental_updates": true,
  "update_chain_length": 3,
  "pdf_version": "1.7",
  "has_digital_signature": false,
  "signature_count": 0,
  "signature_removed": true,
  "modifications_after_signature": false,
  "page_count": 12,
  "object_count": 487,
  "has_javascript": false,
  "has_embedded_files": false,
  "modification_markers": [
    "Digital signature was removed",
    "Different creation and modification dates"
  ]
}

All date fields (check_date, creation_date, modification_date) are Unix timestamps (seconds since epoch).

modification_markers: All modification signals detected, ordered strongest-first

algorithm_version: Version numbers reflect the algorithm in use at the time of analysis. The current version may differ.

Error Responses

json
// 404 Not Found - Check doesn't exist or belongs to another client
{
  "error": "Check not found or access denied",
  "code": "not_found"
}

// 401 Unauthorized - Invalid API key
{
  "error": "Invalid API key. Please check your credentials.",
  "code": "invalid_api_key"
}

List All Checks

GET https://htpbe.tech/api/v1/checks

Description

Retrieve a paginated list of all your PDF check results with flexible filtering options. This endpoint provides raw data access for custom analytics, exports, and advanced reporting. Use it to build dashboards, export data, or perform custom analysis on your PDF checks.

Request Headers

http
Authorization: Bearer YOUR_API_KEY

Query Parameters (All Optional)

limit (1-500, default: 100): Number of results per page

offset (default: 0): Number of results to skip for pagination

tool: Filter by tool name (matches Creator OR Producer)

creator: Filter by Creator tool only

producer: Filter by Producer tool only

status (intact/modified/inconclusive): Filter by verdict

from_date / to_date (Unix timestamp): Filter by check date (when analysis was performed)

Response (200 OK)

json
{
  "data": [
    {
      "id": "a3f5c9d2-1360-48a2-b389-abb346f85d04",
      "filename": "invoice-2024-01.pdf",
      "check_date": 1738368000,
      "status": "modified",
      "metadata_completeness_score": 85,
      "creator": "Microsoft Word for Microsoft 365",
      "producer": "Adobe PDF Library 15.0",
      "file_size": 524288,
      "page_count": 5,
      "pdf_version": "1.7",
      "creation_date": 1735689600,
      "modification_date": 1738281600,
      "has_javascript": false,
      "has_digital_signature": true,
      "has_embedded_files": false,
      "has_incremental_updates": true,
      "update_chain_length": 3,
      "object_count": 234
    }
  ],
  "total": 1250,
  "limit": 100,
  "offset": 0,
  "has_more": true
}

Use cases: Export all data, build custom analytics, discover all tools, filter modified PDFs

Pagination: Usehas_moreto know when to stop

Example:/api/v1/checks?status=modified&limit=200

Error Responses

All errors include an error string and a machine-readable code. Some errors also include a details string with additional context. Requests beyond your monthly quota are charged at overage rates — there is no 429 cutoff.

CodeDescription
400Bad Request — Invalid URL, malformed body, download failed
401Unauthorized — Missing or invalid API key
402Payment Required — No active subscription
403Forbidden — Deactivated key, or test key used with non-test URL
404Not Found — Check ID not found or belongs to a different API key
413Payload Too Large — File exceeds 10 MB
422Unprocessable Entity — Invalid or corrupted PDF
500Internal Server Error — Processing failed

Integration Examples

Get started quickly with these code examples

cURL

bash
# Step 1: Submit PDF for analysis
curl -X POST https://htpbe.tech/api/v1/analyze \
  -H "Authorization: Bearer htpbe_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/document.pdf"}'
# Returns: {"id":"3f9c8b7a-2e1d-4c5f-9b8e-7a6d5c4b3a21"}

# Step 2: Retrieve full results
ID="3f9c8b7a-2e1d-4c5f-9b8e-7a6d5c4b3a21"
curl -s "https://htpbe.tech/api/v1/result/$ID" \
  -H "Authorization: Bearer htpbe_live_..." \
  | jq '.status'

JavaScript/TypeScript (Node.js)

analyze-pdf.ts
// Step 1: Submit PDF for analysis — returns only { id }
async function analyzePDF(fileUrl: string): Promise<string> {
  const response = await fetch('https://htpbe.tech/api/v1/analyze', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HTPBE_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ url: fileUrl })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`API error: ${error.error}`);
  }

  const { id } = await response.json();
  return id;
}

// Step 2: Retrieve full results by ID
async function getResult(id: string) {
  const response = await fetch(`https://htpbe.tech/api/v1/result/${id}`, {
    headers: { 'Authorization': `Bearer ${process.env.HTPBE_API_KEY}` }
  });
  return response.json();
}

// Usage
const id = await analyzePDF('https://example.com/contract.pdf');
const result = await getResult(id);

switch (result.status) {
  case 'modified':
    console.log('Document has been modified!');
    console.log(`Markers: ${result.modification_markers.join(', ')}`);
    break;
  case 'inconclusive':
    if (result.status_reason === 'online_editor_origin') {
      console.log('Cannot verify — processed through online editor');
    } else if (result.status_reason === 'scanned_document') {
      console.log('Cannot verify — scanned document (no text layer)');
    } else {
      console.log('Cannot verify — created with consumer software');
    }
    console.log(`Origin: ${result.origin.type}, software: ${result.origin.software}`);
    break;
  case 'intact':
    console.log('Document is intact');
    break;
}

Python

verify_pdf.py
import requests
import os
from typing import Dict, Any

class HTPBEClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = 'https://htpbe.tech/api/v1'

    def analyze(self, url: str) -> str:
        """Submit PDF for analysis, returns check ID"""
        response = requests.post(
            f'{self.base_url}/analyze',
            headers={
                'Authorization': f'Bearer {self.api_key}',
                'Content-Type': 'application/json'
            },
            json={'url': url}
        )
        response.raise_for_status()
        return response.json()['id']

    def get_result(self, check_id: str) -> Dict[str, Any]:
        """Retrieve full analysis result"""
        response = requests.get(
            f'{self.base_url}/result/{check_id}',
            headers={'Authorization': f'Bearer {self.api_key}'}
        )
        response.raise_for_status()
        return response.json()

# Initialize client
client = HTPBEClient(os.getenv('HTPBE_API_KEY'))

# Two-step analysis
check_id = client.analyze('https://example.com/contract.pdf')
result = client.get_result(check_id)

# Check results
if result['status'] == 'modified':
    print("Document has been modified!")
    print("\nMarkers:")
    for marker in result['modification_markers']:
        print(f"  • {marker}")
elif result['status'] == 'inconclusive':
    reason = result.get('status_reason', '')
    if reason == 'online_editor_origin':
        print("Cannot verify — processed through online editor")
    elif reason == 'scanned_document':
        print("Cannot verify — scanned document (no text layer)")
    else:
        print("Cannot verify — created with consumer software")
else:
    print("Document appears to be original")

PHP

analyze.php
<?php

function analyzePDF($fileUrl, $apiKey) {
    // Step 1: Submit for analysis
    $ch = curl_init('https://htpbe.tech/api/v1/analyze');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json'
        ],
        CURLOPT_POSTFIELDS => json_encode(['url' => $fileUrl])
    ]);
    $response = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($response, true);
    $checkId = $data['id'];

    // Step 2: Retrieve full result
    $ch = curl_init("https://htpbe.tech/api/v1/result/{$checkId}");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey]
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode !== 200) {
        throw new Exception("API request failed: " . $response);
    }

    return json_decode($response, true);
}

// Usage
$result = analyzePDF(
    'https://example.com/contract.pdf',
    getenv('HTPBE_API_KEY')
);

if ($result['status'] === 'modified') {
    echo "Document modified!\n";
    echo "Markers: " . implode(", ", $result['modification_markers']) . "\n";
} elseif ($result['status'] === 'inconclusive') {
    $reason = $result['status_reason'];
    if ($reason === 'online_editor_origin') {
        echo "Cannot verify — processed through online editor\n";
    } elseif ($reason === 'scanned_document') {
        echo "Cannot verify — scanned document (no text layer)\n";
    } else {
        echo "Cannot verify — created with consumer software\n";
    }
} else {
    echo "Document is original\n";
}

Go

main.go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

type AnalyzeRequest struct {
    FileURL string `json:"url"`
}

type AnalyzeResponse struct {
    ID string `json:"id"`
}

type AnalysisResult struct {
    Status              string   `json:"status"`
    ModificationMarkers []string `json:"modification_markers"`
}

func submitAnalysis(fileURL, apiKey string) (string, error) {
    reqBody, _ := json.Marshal(AnalyzeRequest{FileURL: fileURL})

    req, _ := http.NewRequest("POST",
        "https://htpbe.tech/api/v1/analyze",
        bytes.NewBuffer(reqBody))
    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    var data AnalyzeResponse
    json.Unmarshal(body, &data)
    return data.ID, nil
}

func getResult(id, apiKey string) (*AnalysisResult, error) {
    req, _ := http.NewRequest("GET",
        "https://htpbe.tech/api/v1/result/"+id, nil)
    req.Header.Set("Authorization", "Bearer "+apiKey)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    var result AnalysisResult
    json.Unmarshal(body, &result)
    return &result, nil
}

func main() {
    apiKey := os.Getenv("HTPBE_API_KEY")

    id, err := submitAnalysis("https://example.com/contract.pdf", apiKey)
    if err != nil {
        panic(err)
    }

    result, err := getResult(id, apiKey)
    if err != nil {
        panic(err)
    }

    switch result.Status {
    case "modified":
        fmt.Println("Modified!")
        for _, m := range result.ModificationMarkers {
            fmt.Println(" •", m)
        }
    case "inconclusive":
        fmt.Println("Cannot verify — consumer software, online editor, or scanned origin")
    default:
        fmt.Println("Original document")
    }
}

Ruby

analyze.rb
require 'net/http'
require 'json'
require 'uri'

class HTPBEClient
  def initialize(api_key)
    @api_key = api_key
    @base_url = 'https://htpbe.tech/api/v1'
  end

  def analyze(url)
    uri = URI("#{@base_url}/analyze")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri.path)
    request['Authorization'] = "Bearer #{@api_key}"
    request['Content-Type'] = 'application/json'
    request.body = { url: url }.to_json

    response = http.request(request)
    JSON.parse(response.body)['id']
  end

  def get_result(check_id)
    uri = URI("#{@base_url}/result/#{check_id}")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Get.new(uri.path)
    request['Authorization'] = "Bearer #{@api_key}"

    response = http.request(request)
    JSON.parse(response.body)
  end
end

# Usage
client = HTPBEClient.new(ENV['HTPBE_API_KEY'])
check_id = client.analyze('https://example.com/contract.pdf')
result = client.get_result(check_id)

if result['status'] == 'modified'
  puts "Document modified!"
  puts "Markers: #{result['modification_markers'].join(', ')}"
elsif result['status'] == 'inconclusive'
  puts "Cannot verify — consumer software, online editor, or scanned origin"
else
  puts "Document is original"
end

Retrieving Results and Check History

Examples of using the GET endpoints to retrieve check results and check history

Get Check Result by ID

typescript
// Retrieve a specific check result
const checkId = '506a6b1b-1360-48a2-b389-abb346f85d04';

const response = await fetch(
  `https://htpbe.tech/api/v1/result/${checkId}`,
  {
    headers: {
      'Authorization': `Bearer ${API_KEY}`
    }
  }
);

const result = await response.json();

console.log(`File: ${result.filename}`);
console.log(`Status: ${result.status}`);
console.log(`Markers: ${result.modification_markers.join(', ')}`);

Analyze Specific Tool Usage

python
import requests
from urllib.parse import quote

# Get all modified PDFs for manual review
response = requests.get(
    'https://htpbe.tech/api/v1/checks',
    params={
        'status': 'modified',
        'limit': 100
    },
    headers={'Authorization': f'Bearer {API_KEY}'}
)

data = response.json()

print(f"Found {data['total']} modified PDFs")
print(f"\nShowing first {len(data['data'])} results:")

for check in data['data'][:5]:
    print(f"\n{check['filename']}")
    print(f"  Tool: {check['creator']} → {check['producer']}")
    print(f"  Review: https://htpbe.tech/result/{check['id']}")

Building a Dashboard

typescript
// Build a dashboard from /checks — no extra endpoints needed
async function fetchDashboardData(apiKey: string) {
  const headers = { Authorization: `Bearer ${apiKey}` };

  // Fetch all checks (paginate if needed)
  const checksRes = await fetch(
    'https://htpbe.tech/api/v1/checks?limit=500',
    { headers }
  );
  const { data: checks, total } = await checksRes.json();

  // Calculate metrics from raw data
  const modified = checks.filter((c) => c.status === 'modified').length;
  const toolStats = new Map<string, { count: number; modified: number }>();

  checks.forEach((check) => {
    const tool = check.producer || 'Unknown';
    const current = toolStats.get(tool) || { count: 0, modified: 0 };
    toolStats.set(tool, {
      count: current.count + 1,
      modified: current.modified + (check.status === 'modified' ? 1 : 0)
    });
  });

  return {
    overview: {
      total,
      modified,
      modificationRate: total > 0 ? ((modified / total) * 100).toFixed(1) : '0.0'
    },
    recentModified: checks
      .filter((c) => c.status === 'modified')
      .slice(0, 5)
      .map((c) => ({ filename: c.filename, tool: c.producer })),
    toolBreakdown: Array.from(toolStats.entries())
      .map(([name, data]) => ({
        name,
        count: data.count,
        modificationRate: ((data.modified / data.count) * 100).toFixed(1)
      }))
      .sort((a, b) => b.count - a.count)
  };
}

const dashboardData = await fetchDashboardData(API_KEY);
console.log('Dashboard Data:', JSON.stringify(dashboardData, null, 2));

Real-World Use Cases

1. Contract Verification Before Signing

javascript
// Verify contract hasn't been tampered with
async function verifyContract(contractUrl) {
  // Step 1: Submit for analysis
  const { id } = await fetch('https://htpbe.tech/api/v1/analyze', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + process.env.HTPBE_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ url: contractUrl })
  }).then(r => r.json());

  // Step 2: Get full result
  const result = await fetch(`https://htpbe.tech/api/v1/result/${id}`, {
    headers: { 'Authorization': 'Bearer ' + process.env.HTPBE_API_KEY }
  }).then(r => r.json());

  if (result.status === 'modified') {
    return {
      safe: false,
      message: 'Contract has been modified. Do not sign!',
      warnings: result.modification_markers
    };
  }

  if (result.status === 'inconclusive') {
    const reasons = {
      online_editor_origin: 'Processed through online editor — original metadata stripped. Manual review required.',
      scanned_document: 'Scanned document — no text layer to verify. Manual review required.',
      consumer_software_origin: 'Origin unclear — created with consumer software. Manual review required.'
    };
    return {
      safe: false,
      message: reasons[result.status_reason] || 'Origin unclear. Manual review required.',
      warnings: []
    };
  }

  return { safe: true, message: 'Contract is intact and safe to review' };
}

2. Bulk Document Verification

python
import asyncio
import aiohttp

async def analyze_bulk(urls: list[str], api_key: str):
    """Analyze multiple PDFs concurrently"""
    headers = {'Authorization': f'Bearer {api_key}'}

    async with aiohttp.ClientSession() as session:
        # Step 1: Submit all PDFs for analysis
        submit_tasks = [
            session.post(
                'https://htpbe.tech/api/v1/analyze',
                headers={**headers, 'Content-Type': 'application/json'},
                json={'url': url}
            )
            for url in urls
        ]
        submit_responses = await asyncio.gather(*submit_tasks)
        ids = [(await r.json())['id'] for r in submit_responses]

        # Step 2: Retrieve all results
        result_tasks = [
            session.get(
                f'https://htpbe.tech/api/v1/result/{id}',
                headers=headers
            )
            for id in ids
        ]
        result_responses = await asyncio.gather(*result_tasks)
        results = [await r.json() for r in result_responses]

        modified_count = sum(1 for r in results if r['status'] == 'modified')
        inconclusive_count = sum(1 for r in results if r['status'] == 'inconclusive')

        return {
            'total': len(results),
            'modified': modified_count,
            'inconclusive': inconclusive_count,
            'intact': len(results) - modified_count - inconclusive_count,
            'details': results
        }

# Process 100 documents in parallel
urls = [f'https://storage.example.com/doc_{i}.pdf' for i in range(100)]
summary = await analyze_bulk(urls, os.getenv('HTPBE_API_KEY'))
print(f"Scanned {summary['total']} docs: {summary['modified']} modified, {summary['inconclusive']} inconclusive, {summary['intact']} intact")

3. Document Management System Integration

typescript
// Automatic verification on upload
async function handleDocumentUpload(file: File) {
  // 1. Upload to your storage
  const fileUrl = await uploadToS3(file);

  // 2. Submit for analysis
  const { id } = await fetch('https://htpbe.tech/api/v1/analyze', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${HTPBE_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ url: fileUrl })
  }).then(r => r.json());

  // 3. Retrieve full result
  const result = await fetch(`https://htpbe.tech/api/v1/result/${id}`, {
    headers: { 'Authorization': `Bearer ${HTPBE_API_KEY}` }
  }).then(r => r.json());

  // 4. Store in database with verification status
  await db.documents.create({
    filename: file.name,
    url: fileUrl,
    verified: result.status === 'intact',
    uploaded_at: new Date()
  });

  // 5. Alert if modified
  if (result.status === 'modified') {
    await notifySecurityTeam({
      document: file.name,
      findings: result.modification_markers
    });
  }

  return result;
}

LLM-Friendly Documentation

For AI assistants and LLM integration, our API documentation is available in a machine-readable format optimized for language models.

View llms.txt

Don't Trust Blindly — Check Your Document

Our free tool analyzes PDFs to detect modifications.
No registration required. Instant results.

How it WorksAPI