dots-ocr-parser / API_USAGE_GUIDE.md
mac
Add comprehensive API documentation with frontend examples
06ccc5a
|
Raw
History Blame Contribute Delete
26.6 kB

A newer version of the Gradio SDK is available: 6.22.0

Upgrade

DotsOCR API - Complete Usage Guide

Base URL

https://isseygino911-dots-ocr-parser.hf.space

Table of Contents

  1. Quick Start
  2. API Endpoints
  3. Backend Examples (Python)
  4. Frontend Examples (JavaScript)
  5. React Integration
  6. Complete Workflows
  7. Error Handling

Quick Start

Test Your API (Command Line)

# 1. Health check
curl https://isseygino911-dots-ocr-parser.hf.space/health

# 2. Upload an image
curl -X POST https://isseygino911-dots-ocr-parser.hf.space/api/parse/image \
  -F "file=@image.jpg" \
  -F "prompt_mode=prompt_layout_all_en"

# Response: {"job_id": "abc-123-def", "status": "queued"}

# 3. Check status
curl https://isseygino911-dots-ocr-parser.hf.space/api/jobs/abc-123-def/status

# 4. Get results
curl https://isseygino911-dots-ocr-parser.hf.space/api/jobs/abc-123-def/results

API Endpoints

1. Health Check

GET /health

Check if the API is running and GPU is available.

Response:

{
  "status": "healthy",
  "gpu_available": true
}

2. Upload Image

POST /api/parse/image

Upload and parse an image file.

Parameters:

  • file (required): Image file (multipart/form-data)
  • prompt_mode (optional): Parsing mode
    • prompt_layout_all_en (default) - Full layout + text
    • prompt_layout_only_en - Layout only, no text
    • prompt_ocr - Text extraction only

Supported Formats: .jpg, .jpeg, .png, .bmp, .tiff, .tif, .gif, .webp

Response:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}

3. Upload PDF

POST /api/parse/pdf

Upload and parse a PDF document (processes all pages).

Parameters:

  • file (required): PDF file (multipart/form-data)
  • prompt_mode (optional): Same as image endpoint

Response:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}

4. Get Job Status

GET /api/jobs/{job_id}/status

Monitor processing progress.

Response:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "file_type": "pdf",
  "filename": "document.pdf",
  "total_pages": 10,
  "current_page": 5,
  "progress_percent": 50.0,
  "message": "Processing page 5/10...",
  "created_at": "2024-01-01T10:00:00",
  "updated_at": "2024-01-01T10:02:30"
}

Status values:

  • queued - Waiting to be processed
  • processing - Currently processing
  • completed - Successfully completed
  • failed - Error occurred

5. Get Results

GET /api/jobs/{job_id}/results

Retrieve parsed document data.

Response:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "results": {
    "pages": [
      {
        "page_number": 1,
        "markdown": "# Document Title\n\nThis is the extracted text...",
        "json_output": {
          "bboxes": [[100, 200, 500, 250], [100, 300, 500, 400]],
          "labels": ["title", "paragraph"],
          "pred_text": ["Document Title", "This is the extracted text..."]
        },
        "annotated_image_path": "/api/results/550e8400-.../test/page_1.png"
      }
    ]
  },
  "download_url": "/api/jobs/550e8400-e29b-41d4-a716-446655440000/download"
}

6. Download ZIP

GET /api/jobs/{job_id}/download

Download all results as a ZIP file.

Response: Binary ZIP file containing:

  • Annotated images with bounding boxes (PNG)
  • JSON files with structured data
  • Markdown files with extracted text

7. WebSocket Progress Stream

WS /api/jobs/{job_id}/stream

Real-time progress updates via WebSocket.

Message Format:

{
  "event": "status_update",
  "data": {
    "job_id": "550e8400-...",
    "status": "processing",
    "progress_percent": 50.0,
    "message": "Processing page 5/10..."
  }
}

Backend Examples (Python)

Basic Upload and Poll

import requests
import time

API_BASE = "https://isseygino911-dots-ocr-parser.hf.space"

# 1. Upload file
with open("document.pdf", "rb") as f:
    response = requests.post(
        f"{API_BASE}/api/parse/pdf",
        files={"file": f},
        data={"prompt_mode": "prompt_layout_all_en"}
    )

job_id = response.json()["job_id"]
print(f"Job ID: {job_id}")

# 2. Poll for completion
while True:
    status_resp = requests.get(f"{API_BASE}/api/jobs/{job_id}/status")
    status = status_resp.json()

    print(f"Progress: {status['progress_percent']:.0f}% - {status['message']}")

    if status["status"] == "completed":
        break
    elif status["status"] == "failed":
        print(f"Error: {status.get('error')}")
        break

    time.sleep(2)

# 3. Get results
results = requests.get(f"{API_BASE}/api/jobs/{job_id}/results").json()
print(f"Processed {len(results['results']['pages'])} pages")

# 4. Download ZIP
zip_response = requests.get(f"{API_BASE}/api/jobs/{job_id}/download")
with open("results.zip", "wb") as f:
    f.write(zip_response.content)

Using WebSocket for Real-Time Updates

import asyncio
import websockets
import json

async def monitor_job(job_id):
    uri = f"wss://isseygino911-dots-ocr-parser.hf.space/api/jobs/{job_id}/stream"

    async with websockets.connect(uri) as websocket:
        while True:
            message = await websocket.recv()
            data = json.loads(message)

            status = data['data']
            print(f"Status: {status['status']} - {status['progress_percent']:.0f}%")

            if status['status'] in ['completed', 'failed']:
                break

# Usage
asyncio.run(monitor_job("your-job-id"))

Flask Backend Integration

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)
API_BASE = "https://isseygino911-dots-ocr-parser.hf.space"

@app.route('/api/ocr', methods=['POST'])
def ocr_upload():
    # Receive file from your frontend
    if 'file' not in request.files:
        return jsonify({"error": "No file provided"}), 400

    file = request.files['file']

    # Forward to DotsOCR API
    files = {'file': (file.filename, file.stream, file.content_type)}
    data = {'prompt_mode': request.form.get('prompt_mode', 'prompt_layout_all_en')}

    response = requests.post(
        f"{API_BASE}/api/parse/image",
        files=files,
        data=data
    )

    return jsonify(response.json())

@app.route('/api/ocr/status/<job_id>')
def ocr_status(job_id):
    response = requests.get(f"{API_BASE}/api/jobs/{job_id}/status")
    return jsonify(response.json())

if __name__ == '__main__':
    app.run(debug=True)

Frontend Examples (JavaScript)

Vanilla JavaScript - Upload and Monitor

<!DOCTYPE html>
<html>
<head>
    <title>DotsOCR Upload</title>
</head>
<body>
    <h1>Document OCR</h1>

    <input type="file" id="fileInput" accept="image/*,.pdf">
    <button onclick="uploadFile()">Upload</button>

    <div id="status"></div>
    <div id="results"></div>

    <script>
        const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';

        async function uploadFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];

            if (!file) {
                alert('Please select a file');
                return;
            }

            // Determine endpoint based on file type
            const isPDF = file.name.toLowerCase().endsWith('.pdf');
            const endpoint = isPDF ? '/api/parse/pdf' : '/api/parse/image';

            // Upload file
            const formData = new FormData();
            formData.append('file', file);
            formData.append('prompt_mode', 'prompt_layout_all_en');

            const uploadResp = await fetch(`${API_BASE}${endpoint}`, {
                method: 'POST',
                body: formData
            });

            const { job_id } = await uploadResp.json();
            console.log('Job ID:', job_id);

            // Monitor progress
            monitorProgress(job_id);
        }

        async function monitorProgress(jobId) {
            const statusDiv = document.getElementById('status');

            while (true) {
                const response = await fetch(`${API_BASE}/api/jobs/${jobId}/status`);
                const status = await response.json();

                statusDiv.innerHTML = `
                    <p>Status: ${status.status}</p>
                    <p>Progress: ${status.progress_percent.toFixed(0)}%</p>
                    <p>${status.message}</p>
                `;

                if (status.status === 'completed') {
                    await displayResults(jobId);
                    break;
                } else if (status.status === 'failed') {
                    statusDiv.innerHTML += `<p style="color: red;">Error: ${status.error}</p>`;
                    break;
                }

                await new Promise(resolve => setTimeout(resolve, 2000));
            }
        }

        async function displayResults(jobId) {
            const response = await fetch(`${API_BASE}/api/jobs/${jobId}/results`);
            const data = await response.json();

            const resultsDiv = document.getElementById('results');
            resultsDiv.innerHTML = '<h2>Results</h2>';

            data.results.pages.forEach(page => {
                resultsDiv.innerHTML += `
                    <div style="border: 1px solid #ccc; margin: 10px; padding: 10px;">
                        <h3>Page ${page.page_number}</h3>
                        <pre>${page.markdown.substring(0, 500)}...</pre>
                        <a href="${API_BASE}/api/jobs/${jobId}/download" download>
                            Download ZIP
                        </a>
                    </div>
                `;
            });
        }
    </script>
</body>
</html>

Using Fetch API with Async/Await

const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';

// Upload file
async function uploadDocument(file) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('prompt_mode', 'prompt_layout_all_en');

    const endpoint = file.name.endsWith('.pdf')
        ? '/api/parse/pdf'
        : '/api/parse/image';

    const response = await fetch(`${API_BASE}${endpoint}`, {
        method: 'POST',
        body: formData
    });

    return await response.json();
}

// Check status
async function checkStatus(jobId) {
    const response = await fetch(`${API_BASE}/api/jobs/${jobId}/status`);
    return await response.json();
}

// Get results
async function getResults(jobId) {
    const response = await fetch(`${API_BASE}/api/jobs/${jobId}/results`);
    return await response.json();
}

// Complete workflow
async function processDocument(file) {
    try {
        // 1. Upload
        const { job_id } = await uploadDocument(file);
        console.log('Job ID:', job_id);

        // 2. Poll for completion
        while (true) {
            const status = await checkStatus(job_id);
            console.log(`Progress: ${status.progress_percent}%`);

            if (status.status === 'completed') {
                break;
            } else if (status.status === 'failed') {
                throw new Error(status.error);
            }

            await new Promise(resolve => setTimeout(resolve, 2000));
        }

        // 3. Get results
        const results = await getResults(job_id);
        console.log('Results:', results);

        return results;
    } catch (error) {
        console.error('Error:', error);
        throw error;
    }
}

// Usage
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    if (file) {
        const results = await processDocument(file);
        console.log('Processing complete!', results);
    }
});

Using WebSocket for Real-Time Updates

function monitorJobWithWebSocket(jobId, onUpdate, onComplete) {
    const ws = new WebSocket(
        `wss://isseygino911-dots-ocr-parser.hf.space/api/jobs/${jobId}/stream`
    );

    ws.onopen = () => {
        console.log('WebSocket connected');
    };

    ws.onmessage = (event) => {
        const message = JSON.parse(event.data);
        const status = message.data;

        // Call update callback
        onUpdate(status);

        // Check if completed
        if (status.status === 'completed') {
            onComplete(status);
            ws.close();
        } else if (status.status === 'failed') {
            onComplete(status);
            ws.close();
        }
    };

    ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        // Fallback to HTTP polling
        pollJobStatus(jobId, onUpdate, onComplete);
    };

    return ws;
}

// Usage
const ws = monitorJobWithWebSocket(
    'job-id-here',
    (status) => {
        console.log(`Progress: ${status.progress_percent}%`);
        document.getElementById('progress').textContent =
            `${status.progress_percent.toFixed(0)}%`;
    },
    (status) => {
        console.log('Job completed!', status);
        displayResults(status.job_id);
    }
);

React Integration

Complete React Component

import React, { useState, useCallback } from 'react';
import axios from 'axios';

const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';

function DocumentOCR() {
    const [file, setFile] = useState(null);
    const [jobId, setJobId] = useState(null);
    const [status, setStatus] = useState(null);
    const [results, setResults] = useState(null);
    const [uploading, setUploading] = useState(false);

    // Handle file selection
    const handleFileChange = (e) => {
        setFile(e.target.files[0]);
        setJobId(null);
        setStatus(null);
        setResults(null);
    };

    // Upload file
    const handleUpload = async () => {
        if (!file) return;

        setUploading(true);

        const formData = new FormData();
        formData.append('file', file);
        formData.append('prompt_mode', 'prompt_layout_all_en');

        const endpoint = file.name.toLowerCase().endsWith('.pdf')
            ? '/api/parse/pdf'
            : '/api/parse/image';

        try {
            const response = await axios.post(`${API_BASE}${endpoint}`, formData);
            const { job_id } = response.data;
            setJobId(job_id);
            startPolling(job_id);
        } catch (error) {
            console.error('Upload failed:', error);
            alert('Upload failed');
        } finally {
            setUploading(false);
        }
    };

    // Poll for status
    const startPolling = useCallback((jobId) => {
        const interval = setInterval(async () => {
            try {
                const response = await axios.get(`${API_BASE}/api/jobs/${jobId}/status`);
                const statusData = response.data;
                setStatus(statusData);

                if (statusData.status === 'completed') {
                    clearInterval(interval);
                    fetchResults(jobId);
                } else if (statusData.status === 'failed') {
                    clearInterval(interval);
                    alert('Processing failed: ' + statusData.error);
                }
            } catch (error) {
                console.error('Status check failed:', error);
            }
        }, 2000);

        return () => clearInterval(interval);
    }, []);

    // Fetch results
    const fetchResults = async (jobId) => {
        try {
            const response = await axios.get(`${API_BASE}/api/jobs/${jobId}/results`);
            setResults(response.data);
        } catch (error) {
            console.error('Failed to fetch results:', error);
        }
    };

    return (
        <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
            <h1>Document OCR</h1>

            {/* File Upload */}
            <div style={{ marginBottom: '20px' }}>
                <input
                    type="file"
                    accept="image/*,.pdf"
                    onChange={handleFileChange}
                    disabled={uploading}
                />
                <button
                    onClick={handleUpload}
                    disabled={!file || uploading}
                    style={{ marginLeft: '10px' }}
                >
                    {uploading ? 'Uploading...' : 'Upload'}
                </button>
            </div>

            {/* Status */}
            {status && (
                <div style={{
                    padding: '15px',
                    background: '#f0f0f0',
                    borderRadius: '5px',
                    marginBottom: '20px'
                }}>
                    <h3>Status: {status.status}</h3>
                    <div style={{
                        width: '100%',
                        height: '20px',
                        background: '#ddd',
                        borderRadius: '10px',
                        overflow: 'hidden'
                    }}>
                        <div style={{
                            width: `${status.progress_percent}%`,
                            height: '100%',
                            background: '#4CAF50',
                            transition: 'width 0.3s'
                        }} />
                    </div>
                    <p>{status.message}</p>
                    {status.total_pages > 1 && (
                        <p>Page {status.current_page} of {status.total_pages}</p>
                    )}
                </div>
            )}

            {/* Results */}
            {results && (
                <div>
                    <h2>Results ({results.results.pages.length} page(s))</h2>
                    {results.results.pages.map((page) => (
                        <div key={page.page_number} style={{
                            border: '1px solid #ccc',
                            padding: '15px',
                            marginBottom: '15px',
                            borderRadius: '5px'
                        }}>
                            <h3>Page {page.page_number}</h3>

                            {/* Markdown Preview */}
                            <div style={{ marginBottom: '10px' }}>
                                <h4>Extracted Text:</h4>
                                <pre style={{
                                    background: '#f5f5f5',
                                    padding: '10px',
                                    borderRadius: '3px',
                                    whiteSpace: 'pre-wrap'
                                }}>
                                    {page.markdown.substring(0, 500)}
                                    {page.markdown.length > 500 && '...'}
                                </pre>
                            </div>

                            {/* Detected Elements */}
                            <div>
                                <h4>Detected Elements:</h4>
                                <p>
                                    {page.json_output.labels.length} elements found: {' '}
                                    {[...new Set(page.json_output.labels)].join(', ')}
                                </p>
                            </div>
                        </div>
                    ))}

                    {/* Download Button */}
                    <a
                        href={`${API_BASE}${results.download_url}`}
                        download
                        style={{
                            display: 'inline-block',
                            padding: '10px 20px',
                            background: '#2196F3',
                            color: 'white',
                            textDecoration: 'none',
                            borderRadius: '5px'
                        }}
                    >
                        Download ZIP
                    </a>
                </div>
            )}
        </div>
    );
}

export default DocumentOCR;

React with WebSocket

import React, { useState, useEffect } from 'react';

const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';

function useWebSocketProgress(jobId) {
    const [status, setStatus] = useState(null);

    useEffect(() => {
        if (!jobId) return;

        const ws = new WebSocket(
            `wss://isseygino911-dots-ocr-parser.hf.space/api/jobs/${jobId}/stream`
        );

        ws.onmessage = (event) => {
            const message = JSON.parse(event.data);
            setStatus(message.data);
        };

        ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        return () => {
            ws.close();
        };
    }, [jobId]);

    return status;
}

// Usage in component
function MyComponent({ jobId }) {
    const status = useWebSocketProgress(jobId);

    if (!status) return <div>Loading...</div>;

    return (
        <div>
            <p>Status: {status.status}</p>
            <p>Progress: {status.progress_percent}%</p>
        </div>
    );
}

Complete Workflows

Workflow 1: Simple Upload and Download

async function simpleWorkflow(file) {
    const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';

    // 1. Upload
    const formData = new FormData();
    formData.append('file', file);

    const uploadResp = await fetch(`${API_BASE}/api/parse/image`, {
        method: 'POST',
        body: formData
    });
    const { job_id } = await uploadResp.json();

    // 2. Wait for completion
    let status;
    do {
        await new Promise(resolve => setTimeout(resolve, 2000));
        const statusResp = await fetch(`${API_BASE}/api/jobs/${job_id}/status`);
        status = await statusResp.json();
    } while (status.status === 'processing' || status.status === 'queued');

    // 3. Download results
    const downloadUrl = `${API_BASE}/api/jobs/${job_id}/download`;
    window.open(downloadUrl, '_blank');
}

Workflow 2: Batch Processing

async function processBatch(files) {
    const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';
    const results = [];

    for (const file of files) {
        const formData = new FormData();
        formData.append('file', file);

        const uploadResp = await fetch(`${API_BASE}/api/parse/image`, {
            method: 'POST',
            body: formData
        });
        const { job_id } = await uploadResp.json();

        results.push({ filename: file.name, job_id });
    }

    // Monitor all jobs
    const completed = [];
    while (completed.length < results.length) {
        for (const job of results) {
            if (completed.includes(job.job_id)) continue;

            const statusResp = await fetch(`${API_BASE}/api/jobs/${job.job_id}/status`);
            const status = await statusResp.json();

            if (status.status === 'completed') {
                completed.push(job.job_id);
                console.log(`${job.filename} completed`);
            }
        }

        await new Promise(resolve => setTimeout(resolve, 2000));
    }

    return results;
}

Error Handling

Best Practices

async function robustUpload(file) {
    const API_BASE = 'https://isseygino911-dots-ocr-parser.hf.space';
    const MAX_RETRIES = 3;
    const TIMEOUT = 300000; // 5 minutes

    try {
        // 1. Upload with retry
        let uploadResp;
        for (let i = 0; i < MAX_RETRIES; i++) {
            try {
                const formData = new FormData();
                formData.append('file', file);

                uploadResp = await fetch(`${API_BASE}/api/parse/image`, {
                    method: 'POST',
                    body: formData
                });

                if (uploadResp.ok) break;

                if (i === MAX_RETRIES - 1) {
                    throw new Error('Upload failed after retries');
                }

                await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
            } catch (error) {
                if (i === MAX_RETRIES - 1) throw error;
            }
        }

        const { job_id } = await uploadResp.json();

        // 2. Poll with timeout
        const startTime = Date.now();
        while (true) {
            if (Date.now() - startTime > TIMEOUT) {
                throw new Error('Processing timeout');
            }

            const statusResp = await fetch(`${API_BASE}/api/jobs/${job_id}/status`);

            if (!statusResp.ok) {
                throw new Error(`Status check failed: ${statusResp.status}`);
            }

            const status = await statusResp.json();

            if (status.status === 'completed') {
                return await fetch(`${API_BASE}/api/jobs/${job_id}/results`)
                    .then(r => r.json());
            }

            if (status.status === 'failed') {
                throw new Error(`Processing failed: ${status.error}`);
            }

            await new Promise(resolve => setTimeout(resolve, 2000));
        }

    } catch (error) {
        console.error('Error:', error);

        // Handle specific errors
        if (error.message.includes('timeout')) {
            alert('Processing is taking longer than expected. Please try again later.');
        } else if (error.message.includes('failed')) {
            alert('Processing failed. Please check your file and try again.');
        } else {
            alert('An error occurred. Please try again.');
        }

        throw error;
    }
}

Performance Tips

  1. Use WebSocket for large PDFs - More efficient than polling for documents with many pages
  2. Poll interval - Use 2-3 seconds for polling to balance responsiveness and server load
  3. Timeout handling - Set appropriate timeouts based on file size (60s for images, 300s for PDFs)
  4. Batch processing - Process files sequentially to avoid overwhelming the server
  5. Error recovery - Implement retry logic with exponential backoff

Support

For issues or questions:

  • API Documentation: https://isseygino911-dots-ocr-parser.hf.space/docs
  • Test your integration at: Interactive API Docs

Built with DotsOCR - 1.7B parameter Vision-Language Model for Document Understanding