Direct Upload
Create a job and generate presigned URLs for direct file upload to S3
Direct Upload
Create a job and generate presigned URLs for direct file upload to S3. Uses multipart upload for all files (1 part for small files, multiple parts for large files ≥50MB).
Endpoint
POST /ocr/uploads/directParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | PDF file to process |
output_type | string | No | Output format: structured, markdown, per_page_structured |
model | string | No | Model to use (default: standard-v1) |
instruction | string | No | Processing instructions (cannot use with schema or template_slug) |
schema | string | No | JSON schema as string (cannot use with instruction) |
template_slug | string | No | Document template slug (cannot use with instruction or schema) |
Note: Only one of template_slug, schema, or instruction can be
provided per request.
Output Types
structured: Structured data extraction. Requires eithertemplate_slugOR format (withschema&instructions)markdown: Page-by-page OCR. All configuration fields are optionalper_page_structured: Per-page structured extraction
SDK Examples
import { LeapOCR } from "leapocr";
const client = new LeapOCR({
apiKey: process.env.LEAPOCR_API_KEY,
});
// Upload a local file
const job = await client.ocr.processFile("./document.pdf");
console.log(`Job created: ${job.jobId}`);
// Wait for processing to complete
const result = await client.ocr.waitUntilDone(job.jobId);
console.log("Processing complete!");# Note: The SDKs handle the multipart upload automatically.
# For direct HTTP usage, see the "Manual Implementation" section below.
# Step 1: Initiate direct upload
curl -X POST https://api.leapocr.com/api/v1/ocr/uploads/direct \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"content_type": "application/pdf",
"file_size": 1048576
}'import asyncio
import os
from pathlib import Path
from leapocr import LeapOCR
async def main():
async with LeapOCR(os.getenv("LEAPOCR_API_KEY")) as client:
# Upload a local file
job = await client.ocr.process_file(Path("document.pdf"))
print(f"Job created: {job.job_id}")
# Wait for processing to complete
result = await client.ocr.wait_until_done(job.job_id)
print("Processing complete!")
asyncio.run(main())package main
import (
"context"
"fmt"
"log"
"os"
"github.com/leapocr/leapocr-go"
)
func main() {
client, err := ocr.New(os.Getenv("LEAPOCR_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Upload a local file
file, err := os.Open("document.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
job, err := client.ProcessFile(ctx, file, "document.pdf")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Job created: %s\n", job.ID)
// Wait for processing to complete
result, err := client.WaitUntilDone(ctx, job.ID)
if err != nil {
log.Fatal(err)
}
fmt.Println("Processing complete!")
}Response
{
"job_id": "job_abc123"
}Error Responses
| Status Code | Description |
|---|---|
400 | Bad Request |
401 | Unauthorized |
402 | Insufficient credits |
500 | Internal Server Error |
Using Templates
You can use pre-configured templates for common document types:
const job = await client.ocr.processFile(
"./document.pdf",
{
templateSlug: "invoice-extraction",
}
);curl -X POST https://api.leapocr.com/api/v1/ocr/uploads/direct \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"content_type": "application/pdf",
"file_size": 1048576,
"template_slug": "invoice-extraction"
}'from leapocr import ProcessOptions
job = await client.ocr.process_file(
Path("document.pdf"),
options=ProcessOptions(
template_slug="invoice-extraction"
),
)file, err := os.Open("document.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
job, err := client.ProcessFile(ctx, file, "document.pdf",
ocr.WithTemplateSlug("invoice-extraction"),
)Custom Schema Extraction
Define custom extraction schemas with instructions for specific use cases:
const job = await client.ocr.processFile(
"./document.pdf",
{
format: "structured",
schema: {
type: "object",
properties: {
invoice_number: { type: "string" },
date: { type: "string" },
total_amount: { type: "number" },
},
},
instructions: "Extract invoice details from the document",
}
);curl -X POST https://api.leapocr.com/api/v1/ocr/uploads/direct \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"content_type": "application/pdf",
"file_size": 1048576,
"format": "structured",
"schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"date": {"type": "string"},
"total_amount": {"type": "number"}
}
},
"instructions": "Extract invoice details from the document"
}'from leapocr import ProcessOptions, Format
job = await client.ocr.process_file(
Path("document.pdf"),
options=ProcessOptions(
format=Format.STRUCTURED,
schema={
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"date": {"type": "string"},
"total_amount": {"type": "number"},
},
},
instructions="Extract invoice details from the document",
),
)file, err := os.Open("document.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"invoice_number": map[string]string{"type": "string"},
"date": map[string]string{"type": "string"},
"total_amount": map[string]string{"type": "number"},
},
}
job, err := client.ProcessFile(ctx, file, "document.pdf",
ocr.WithFormat(ocr.FormatStructured),
ocr.WithSchema(schema),
ocr.WithInstructions("Extract invoice details from the document"),
)Complete Upload (Advanced)
Note: The SDKs handle multipart upload completion automatically. You typically don't need to call this endpoint directly unless you're implementing custom upload logic.
After uploading file chunks to S3, you need to complete the upload by providing all part ETags.
Endpoint
POST /ocr/uploads/{job_id}/completeHow It Works
- Initiate Upload: Call the Direct Upload endpoint to get presigned URLs
- Upload Parts: Upload file parts to the presigned URLs (S3 returns ETags)
- Complete Upload: Call the complete endpoint with all part ETags
- Processing Begins: The job automatically starts processing
Manual Implementation
If you're implementing direct uploads without the SDK, follow this flow:
Step 1: Initiate Upload
curl -X POST https://api.leapocr.com/api/v1/ocr/uploads/direct \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"file_size": 1048576,
"output_type": "structured"
}'Response:
{
"job_id": "job_abc123",
"upload_id": "upload_xyz789",
"parts": [
{
"part_number": 1,
"upload_url": "https://s3.amazonaws.com/..."
}
]
}Step 2: Upload Parts to S3
# Upload each part and capture the ETag from response headers
curl -X PUT "https://s3.amazonaws.com/..." \
--upload-file document-part1.pdf \
-D - | grep -i etagStep 3: Complete Upload
curl -X POST https://api.leapocr.com/api/v1/ocr/uploads/job_abc123/complete \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"parts": [
{
"part_number": 1,
"etag": "\"abc123def456...\""
}
]
}'Important: - ETags must include surrounding quotes: "abc123def456..." -
Parts must be provided in sequential order - All parts from the upload must be
included
Multipart Upload Details
File Size Thresholds
- < 50MB: Single part upload
- ≥ 50MB: Multiple parts (5MB per part)
ETag Format
ETags are returned by S3 in the response headers and must be provided exactly as received:
ETag: "abc123def456789..."The quotes are part of the ETag value and must be included in the completion request.
Next Steps
- Get Job Status - Check processing progress
- Get Job Result - Retrieve the extracted data
- Delete Job - Remove sensitive data