A newer version of the Gradio SDK is available: 6.22.0
DotsOCR API - Complete Usage Guide
Base URL
https://isseygino911-dots-ocr-parser.hf.space
Table of Contents
- Quick Start
- API Endpoints
- Backend Examples (Python)
- Frontend Examples (JavaScript)
- React Integration
- Complete Workflows
- 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 modeprompt_layout_all_en(default) - Full layout + textprompt_layout_only_en- Layout only, no textprompt_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 processedprocessing- Currently processingcompleted- Successfully completedfailed- 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
- Use WebSocket for large PDFs - More efficient than polling for documents with many pages
- Poll interval - Use 2-3 seconds for polling to balance responsiveness and server load
- Timeout handling - Set appropriate timeouts based on file size (60s for images, 300s for PDFs)
- Batch processing - Process files sequentially to avoid overwhelming the server
- 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