API Documentation

Complete guide with code examples in multiple languages

🚀 Quick Start

Get Started in 3 Steps

  1. Sign up and get your API key from the API Keys page
  2. Request a signed upload URL with your desired OCR mode
  3. Upload your image and poll for results

Base URL

https://api.deepreadocr.com/api/v1

Supported Image Formats

PNG JPEG BMP TIFF PDF

🔐 Authentication

All API requests require authentication using an API key in the request header.

X-API-Key: ocr_your_api_key_here

Account Tiers

Tier Monthly Limit Duration
Trial 1,000 requests 30 days
Standard 1,000 requests Monthly
Pro 10,000 requests Monthly
Enterprise Unlimited Custom

📡 API Endpoints

List Available OCR Modes

GET /ocr-modes

Get all OCR modes available for your account, including custom modes.

curl -X GET "https://api.deepreadocr.com/api/v1/ocr-modes" \
  -H "X-API-Key: your_api_key_here"
import requests

response = requests.get(
    "https://api.deepreadocr.com/api/v1/ocr-modes",
    headers={"X-API-Key": "your_api_key_here"}
)
modes = response.json()
print(modes)
const axios = require('axios');

const response = await axios.get(
  'https://api.deepreadocr.com/api/v1/ocr-modes',
  { headers: { 'X-API-Key': 'your_api_key_here' } }
);
console.log(response.data);

Response Example

[
  {
    "name": "ocr",
    "display_name": "General OCR",
    "description": "Extract all text from the image",
    "is_default": true
  },
  {
    "name": "table",
    "display_name": "Table Recognition",
    "description": "Extract tables and structured data",
    "is_default": false
  }
]

Get Upload Signed URL

POST /upload/signed-url

Request a pre-signed S3 URL for uploading your image.

Query Parameters

mode (optional) OCR mode to use. Defaults to your account's default mode.

Request Body

{
  "filename": "document.png",
  "content_type": "image/png"
}
curl -X POST "https://api.deepreadocr.com/api/v1/upload/signed-url?mode=ocr" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "document.png",
    "content_type": "image/png"
  }'
import requests

response = requests.post(
    "https://api.deepreadocr.com/api/v1/upload/signed-url",
    headers={"X-API-Key": "your_api_key_here"},
    params={"mode": "ocr"},
    json={
        "filename": "document.png",
        "content_type": "image/png"
    }
)
data = response.json()
print(data)
const axios = require('axios');

const response = await axios.post(
  'https://api.deepreadocr.com/api/v1/upload/signed-url?mode=ocr',
  {
    filename: 'document.png',
    content_type: 'image/png'
  },
  { headers: { 'X-API-Key': 'your_api_key_here' } }
);
console.log(response.data);

Response Example

{
  "upload_url": "https://s3.amazonaws.com/bucket/uploads/...",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "s3_key": "uploads/user123/550e8400-.../document.png",
  "expires_in": 3600
}

Upload Image to S3

PUT {upload_url}

Upload your image file to the pre-signed URL from the previous step.

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/png" \
  --data-binary "@document.png"
import requests

with open("document.png", "rb") as f:
    requests.put(
        upload_url,
        headers={"Content-Type": "image/png"},
        data=f
    )
const fs = require('fs');
const axios = require('axios');

const imageBuffer = fs.readFileSync('document.png');
await axios.put(uploadUrl, imageBuffer, {
  headers: { 'Content-Type': 'image/png' }
});

List Processing Jobs

GET /processing-jobs

List all processing jobs with their status. Filter by mode or filename.

Query Parameters

mode (optional) Filter by OCR mode (e.g., "table", "ocr")
filename (optional) Filter by filename (partial match)
# List all jobs
curl -X GET "https://api.deepreadocr.com/api/v1/processing-jobs" \
  -H "X-API-Key: your_api_key_here"

# Filter by mode
curl -X GET "https://api.deepreadocr.com/api/v1/processing-jobs?mode=table" \
  -H "X-API-Key: your_api_key_here"

# Filter by filename
curl -X GET "https://api.deepreadocr.com/api/v1/processing-jobs?filename=invoice" \
  -H "X-API-Key: your_api_key_here"
import requests

# List all jobs
response = requests.get(
    "https://api.deepreadocr.com/api/v1/processing-jobs",
    headers={"X-API-Key": "your_api_key_here"}
)
jobs = response.json()

# Filter by mode
response = requests.get(
    "https://api.deepreadocr.com/api/v1/processing-jobs",
    headers={"X-API-Key": "your_api_key_here"},
    params={"mode": "table", "filename": "invoice"}
)
filtered_jobs = response.json()
const axios = require('axios');

// List all jobs
const response = await axios.get(
  'https://api.deepreadocr.com/api/v1/processing-jobs',
  { headers: { 'X-API-Key': 'your_api_key_here' } }
);

// Filter by mode and filename
const filtered = await axios.get(
  'https://api.deepreadocr.com/api/v1/processing-jobs',
  { 
    headers: { 'X-API-Key': 'your_api_key_here' },
    params: { mode: 'table', filename: 'invoice' }
  }
);

Response Example

{
  "files": [
    {
      "filename": "sales_table.png",
      "mode": "table",
      "status": "completed",
      "output_key": "table/sales_table.json",
      "size": 18826,
      "last_modified": "2025-11-17T14:42:48+00:00"
    }
  ],
  "total_count": 1,
  "completed_count": 1,
  "pending_count": 0
}

Download Result File

POST /download/signed-url

Get a signed URL to download the OCR result. Use the simplified output_key from processing-jobs.

Query Parameters

file_path Use the output_key from processing-jobs (e.g., "table/sales_table.json")
curl -X POST "https://api.deepreadocr.com/api/v1/download/signed-url?file_path=table/sales_table.json" \
  -H "X-API-Key: your_api_key_here"

# Response includes signed URL
# Download the file using the URL
curl -o result.json "$DOWNLOAD_URL"

Response Example

{
  "url": "https://s3.amazonaws.com/deepreadocr/...?signature=...",
  "file_key": "client-uuid/output/table/sales_table.json",
  "expires_in": 3600
}

🎯 OCR Modes

Choose the right OCR mode for your use case. You can also create custom modes in your dashboard.

💡 Multi-Mode Processing

Process the same image with different modes! Each mode creates a separate result file with the mode in the path: ocr/document.json, table/document.json, formula/document.json

📄 General OCR

Extract all text from documents, receipts, and forms.

mode=ocr

📊 Table Recognition

Extract tables and structured data from spreadsheets.

mode=table

🔢 Formula Recognition

Extract mathematical formulas in LaTeX format.

mode=formula

📈 Chart Recognition

Extract data from charts and visualizations.

mode=chart

📝 Document Analysis

Analyze and format documents as structured Markdown.

mode=document

✨ Custom Modes

Create your own modes with custom prompts.

Create in Dashboard →

⚡ Rate Limits & Usage

Requests are tracked per calendar month. When your limit is exceeded, the API returns 403 Forbidden.

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 2024-02-01T00:00:00Z

✓ Check your current usage in the dashboard

✓ Upgrade your tier for higher limits

✓ Implement exponential backoff for retry logic

⚠️ Error Handling

HTTP Status Codes

200 Success - Request completed successfully
400 Bad Request - Invalid parameters or request body
401 Unauthorized - Invalid or missing API key
403 Forbidden - Rate limit exceeded or trial expired
404 Not Found - Job ID doesn't exist
429 Too Many Requests - Rate limit hit
500 Internal Server Error - Something went wrong

Error Response Format

{
  "detail": "Error message describing what went wrong"
}

💻 Complete Code Examples

🐍 Python (Complete Workflow)

import requests
import time
from pathlib import Path

class DeepReadOCR:
    def __init__(self, api_key, base_url="https://api.deepreadocr.com/api/v1"):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {"X-API-Key": api_key}
    
    def list_modes(self):
        """List available OCR modes."""
        response = requests.get(f"{self.base_url}/ocr-modes", headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def process_image(self, image_path, mode="ocr", poll_interval=2):
        """Process an image with OCR and return results."""
        filename = Path(image_path).name
        content_type = self._get_content_type(Path(image_path).suffix)
        
        # Step 1: Get upload URL
        response = requests.post(
            f"{self.base_url}/upload/signed-url",
            headers=self.headers,
            params={"mode": mode}
        )
        response.raise_for_status()
        upload_data = response.json()
        
        # Step 2: Upload to S3
        with open(image_path, "rb") as f:
            form_data = upload_data['fields'].copy()
            # Replace placeholder in key
            key = form_data.get('key', '').replace('${filename}', filename)
            form_data['key'] = key
            form_data['Content-Type'] = content_type
            
            requests.post(
                upload_data['url'],
                data=form_data,
                files={'file': (filename, f, content_type)}
            )
        
        # Step 3: Poll for completion
        base_name = filename.rsplit('.', 1)[0]
        while True:
            response = requests.get(
                f"{self.base_url}/processing-jobs",
                headers=self.headers,
                params={"filename": base_name, "mode": mode}
            )
            jobs = response.json()
            
            # Find our job
            for job in jobs['files']:
                if job['status'] == 'completed' and job['output_key']:
                    # Step 4: Download result
                    dl_resp = requests.post(
                        f"{self.base_url}/download/signed-url",
                        headers=self.headers,
                        params={"file_path": job['output_key']}
                    )
                    download_url = dl_resp.json()['url']
                    result = requests.get(download_url).json()
                    return result
            
            time.sleep(poll_interval)
    
    def _get_content_type(self, extension):
        types = {
            ".png": "image/png",
            ".jpg": "image/jpeg",
            ".jpeg": "image/jpeg",
            ".bmp": "image/bmp",
            ".pdf": "application/pdf"
        }
        return types.get(extension.lower(), "application/octet-stream")

# Usage
ocr = DeepReadOCR(api_key="your_api_key_here")

# List modes
modes = ocr.list_modes()
print("Available modes:", [m["name"] for m in modes])

# Process an image
result = ocr.process_image("document.png", mode="table")
print("Mode used:", result["mode"])
print("Extracted text:", result["extracted_text"])
print("Tokens:", result["usage"]["total_tokens"])

🟢 Node.js (Complete Workflow)

const axios = require('axios');
const fs = require('fs');
const path = require('path');
const FormData = require('form-data');

class DeepReadOCR {
  constructor(apiKey, baseURL = 'https://api.deepreadocr.com/api/v1') {
    this.apiKey = apiKey;
    this.baseURL = baseURL;
    this.headers = { 'X-API-Key': apiKey };
  }

  async listModes() {
    const response = await axios.get(`${this.baseURL}/ocr-modes`, {
      headers: this.headers
    });
    return response.data;
  }

  async processImage(imagePath, mode = 'ocr', pollInterval = 2000) {
    const fileName = path.basename(imagePath);
    const contentType = this.getContentType(path.extname(imagePath));
    
    // Get upload URL
    const uploadResponse = await axios.post(
      `${this.baseURL}/upload/signed-url?mode=${mode}`,
      null,
      { headers: this.headers }
    );
    
    const { url, fields, key_prefix } = uploadResponse.data;
    
    // Upload to S3
    const form = new FormData();
    Object.keys(fields).forEach(key => {
      let value = fields[key];
      if (key === 'key') value = value.replace('${filename}', fileName);
      form.append(key, value);
    });
    form.append('Content-Type', contentType);
    form.append('file', fs.createReadStream(imagePath));
    
    await axios.post(url, form, { headers: form.getHeaders() });
    
    // Poll for completion
    const baseName = fileName.replace(/\.[^/.]+$/, '');
    while (true) {
      const jobsResp = await axios.get(`${this.baseURL}/processing-jobs`, {
        headers: this.headers,
        params: { filename: baseName, mode }
      });
      
      const job = jobsResp.data.files.find(j => 
        j.status === 'completed' && j.mode === mode
      );
      
      if (job) {
        // Download result
        const dlResp = await axios.post(
          `${this.baseURL}/download/signed-url`,
          null,
          { headers: this.headers, params: { file_path: job.output_key } }
        );
        
        const result = await axios.get(dlResp.data.url);
        return result.data;
      }
      
      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }
  }

  getContentType(extension) {
    const types = {
      '.png': 'image/png',
      '.jpg': 'image/jpeg',
      '.jpeg': 'image/jpeg',
      '.bmp': 'image/bmp',
      '.pdf': 'application/pdf'
    };
    return types[extension.toLowerCase()] || 'application/octet-stream';
  }
}

// Usage
(async () => {
  const ocr = new DeepReadOCR('your_api_key_here');
  
  const modes = await ocr.listModes();
  console.log('Available modes:', modes.map(m => m.name));
  
  const result = await ocr.processImage('./document.png', 'table');
  console.log('Mode used:', result.mode);
  console.log('Extracted text:', result.extracted_text);
  console.log('Tokens:', result.usage.total_tokens);
})();

🔧 cURL (Bash Script)

#!/bin/bash

API_KEY="your_api_key_here"
BASE_URL="https://api.deepreadocr.com/api/v1"
IMAGE_FILE="document.png"
MODE="table"

# Get upload URL
UPLOAD_RESPONSE=$(curl -s -X POST "${BASE_URL}/upload/signed-url?mode=${MODE}" \
  -H "X-API-Key: ${API_KEY}")

UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.url')
KEY=$(echo "$UPLOAD_RESPONSE" | jq -r '.fields.key' | sed "s/\${filename}/${IMAGE_FILE}/")

echo "Uploading to: ${KEY}"

# Upload to S3 (multipart form)
curl -X POST "${UPLOAD_URL}" \
  -F "key=${KEY}" \
  -F "AWSAccessKeyId=$(echo "$UPLOAD_RESPONSE" | jq -r '.fields.AWSAccessKeyId')" \
  -F "policy=$(echo "$UPLOAD_RESPONSE" | jq -r '.fields.policy')" \
  -F "signature=$(echo "$UPLOAD_RESPONSE" | jq -r '.fields.signature')" \
  -F "Content-Type=image/png" \
  -F "file=@${IMAGE_FILE}"

# Poll for completion
BASE_NAME="${IMAGE_FILE%.*}"
while true; do
  JOBS=$(curl -s "${BASE_URL}/processing-jobs?filename=${BASE_NAME}&mode=${MODE}" \
    -H "X-API-Key: ${API_KEY}")
  
  STATUS=$(echo "$JOBS" | jq -r '.files[0].status')
  echo "Status: ${STATUS}"
  
  if [ "$STATUS" = "completed" ]; then
    OUTPUT_KEY=$(echo "$JOBS" | jq -r '.files[0].output_key')
    
    # Get download URL
    DOWNLOAD=$(curl -s -X POST "${BASE_URL}/download/signed-url?file_path=${OUTPUT_KEY}" \
      -H "X-API-Key: ${API_KEY}")
    
    DOWNLOAD_URL=$(echo "$DOWNLOAD" | jq -r '.url')
    
    # Download and display result
    curl -s "$DOWNLOAD_URL" | jq
    break
  fi
  
  sleep 2
done
🐘 PHP (Click to expand)
apiKey = $apiKey;
        $this->baseURL = $baseURL;
    }
    
    public function processImage($imagePath, $mode = 'ocr') {
        // Get upload URL
        $fileName = basename($imagePath);
        $contentType = $this->getContentType($imagePath);
        
        $ch = curl_init($this->baseURL . '/upload/signed-url?mode=' . $mode);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
            'filename' => $fileName,
            'content_type' => $contentType
        ]));
        
        $response = curl_exec($ch);
        curl_close($ch);
        $uploadData = json_decode($response, true);
        
        $jobId = $uploadData['job_id'];
        
        // Upload to S3
        $imageData = file_get_contents($imagePath);
        $ch = curl_init($uploadData['upload_url']);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        curl_setopt($ch, CURLOPT_POSTFIELDS, $imageData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: ' . $contentType]);
        curl_exec($ch);
        curl_close($ch);
        
        // Poll for results
        while (true) {
            $ch = curl_init($this->baseURL . '/results/' . $jobId);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
            
            $response = curl_exec($ch);
            curl_close($ch);
            $result = json_decode($response, true);
            
            if ($result['status'] === 'completed') return $result;
            if ($result['status'] === 'failed') throw new Exception($result['error']);
            
            sleep(2);
        }
    }
    
    private function getContentType($path) {
        $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
        $types = ['png' => 'image/png', 'jpg' => 'image/jpeg', 'pdf' => 'application/pdf'];
        return $types[$ext] ?? 'application/octet-stream';
    }
}

$ocr = new DeepReadOCR('your_api_key_here');
$result = $ocr->processImage('document.png');
echo $result['result']['extracted_text'];
🔷 Go (Click to expand)
package main

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

type DeepReadOCR struct {
	APIKey  string
	BaseURL string
	Client  *http.Client
}

func NewDeepReadOCR(apiKey string) *DeepReadOCR {
	return &DeepReadOCR{
		APIKey:  apiKey,
		BaseURL: "https://api.deepreadocr.com/api/v1",
		Client:  &http.Client{Timeout: 30 * time.Second},
	}
}

func (ocr *DeepReadOCR) ProcessImage(imagePath, mode string) (map[string]interface{}, error) {
	// Get upload URL
	body, _ := json.Marshal(map[string]string{
		"filename":     "document.png",
		"content_type": "image/png",
	})
	
	req, _ := http.NewRequest("POST", 
		fmt.Sprintf("%s/upload/signed-url?mode=%s", ocr.BaseURL, mode),
		bytes.NewBuffer(body))
	req.Header.Set("X-API-Key", ocr.APIKey)
	req.Header.Set("Content-Type", "application/json")
	
	resp, _ := ocr.Client.Do(req)
	var uploadData map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&uploadData)
	resp.Body.Close()
	
	// Upload to S3
	imageData, _ := os.ReadFile(imagePath)
	s3Req, _ := http.NewRequest("PUT", uploadData["upload_url"].(string), bytes.NewBuffer(imageData))
	s3Req.Header.Set("Content-Type", "image/png")
	ocr.Client.Do(s3Req)
	
	// Poll for results
	jobID := uploadData["job_id"].(string)
	for {
		req, _ := http.NewRequest("GET", fmt.Sprintf("%s/results/%s", ocr.BaseURL, jobID), nil)
		req.Header.Set("X-API-Key", ocr.APIKey)
		
		resp, _ := ocr.Client.Do(req)
		var result map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&result)
		resp.Body.Close()
		
		if result["status"] == "completed" {
			return result, nil
		}
		
		time.Sleep(2 * time.Second)
	}
}

func main() {
	ocr := NewDeepReadOCR("your_api_key_here")
	result, _ := ocr.ProcessImage("document.png", "ocr")
	fmt.Println(result)
}
💎 Ruby (Click to expand)
require 'net/http'
require 'json'

class DeepReadOCR
  def initialize(api_key, base_url = 'https://api.deepreadocr.com/api/v1')
    @api_key = api_key
    @base_url = base_url
  end

  def process_image(image_path, mode = 'ocr')
    # Get upload URL
    uri = URI("#{@base_url}/upload/signed-url?mode=#{mode}")
    request = Net::HTTP::Post.new(uri)
    request['X-API-Key'] = @api_key
    request['Content-Type'] = 'application/json'
    request.body = { filename: File.basename(image_path), content_type: 'image/png' }.to_json

    response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(request) }
    upload_data = JSON.parse(response.body)

    # Upload to S3
    uri = URI(upload_data['upload_url'])
    request = Net::HTTP::Put.new(uri)
    request['Content-Type'] = 'image/png'
    request.body = File.read(image_path)
    Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }

    # Poll for results
    loop do
      uri = URI("#{@base_url}/results/#{upload_data['job_id']}")
      request = Net::HTTP::Get.new(uri)
      request['X-API-Key'] = @api_key

      response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(request) }
      result = JSON.parse(response.body)

      return result if result['status'] == 'completed'
      sleep 2
    end
  end
end

ocr = DeepReadOCR.new('your_api_key_here')
result = ocr.process_image('document.png')
puts result['result']['extracted_text']