FormRead Server-Side Processing API
Server-Side Form Processing
The FormRead Server-Side API lets you send a scanned form image and receive structured results (OMR, OCR, BCR) directly as JSON. No browser, no iframe — just a simple HTTP request from your backend to ours.
- Send an image, get results back as JSON in a single request
- Supports OMR (bubble detection), OCR (text extraction), and BCR (barcode/QR reading)
- Form configuration is loaded automatically from the form you set up in the dashboard
Use the server-side API when you need automated, server-to-server processing without any user interface — for example, processing batches of scanned forms from a folder, integrating with your existing backend pipeline, or building automated grading systems.
If you need a visual interface where users can set up forms, review results, and do QA — use the Iframe Integration API instead.
Authentication
For enterprise uses the API can be used by generating and Bearer Auth token.
Login in the FormRead dashboard and access the API Token option:
Create the API tokens to allow third-party services to authenticate with our application on your behalf:
Process a Form Image
Send a scanned form image to be processed. The form must already be set up with read areas (OMR, OCR, BCR) via the dashboard or iframe. The API uses the saved form configuration to process the image.
Headers
| Name | Value |
|---|---|
Authorization |
Bearer YOUR_API_TOKEN |
Accept |
application/json |
Content-Type |
application/json or multipart/form-data |
With file upload:
curl --location 'https://formread.org/api/forms/123/process' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--form 'image=@"/path/to/scanned-form.jpg"'
With image URL:
curl --location 'https://formread.org/api/forms/123/process' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{"image_url": "https://example.com/scanned-form.jpg"}'
Response
{
"success": true,
"results": {
"OMR-0-0": "A",
"OMR-0-1": "C",
"OMR-0-2": "B,D",
"OCR-1": "John Smith",
"BCR-2": "1234567890"
},
"omr_details": {
"OMR-0": {
"questions": [
[
{"left": 0.12, "top": 0.22, "width": 0.02, "height": 0.02, "blackPixelsRatio": 0.72},
{"left": 0.15, "top": 0.22, "width": 0.02, "height": 0.02, "blackPixelsRatio": 0.15}
]
]
}
},
"anchor_error": false,
"processing_time_ms": 450
}
Response Fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Whether the processing completed successfully |
results |
object | Key-value pairs of area results. Keys are area names (e.g., OMR-0-0, OCR-1, BCR-2) |
omr_details |
object | Detailed bubble-level data for OMR areas including positions and fill ratios |
anchor_error |
boolean | True if the form sheet could not be aligned (e.g., corners not detected) |
processing_time_ms |
integer | Processing time in milliseconds |
Image Input Options
You can provide the image in three ways. Use exactly one:
| Parameter | Type | Description |
|---|---|---|
image |
file | File upload (multipart/form-data) |
image_url |
string | Public URL to the image |
image_base64 |
string | Base64-encoded image data |
The form configuration (read areas, thresholds, anchors) is automatically loaded from the form you set up in the FormRead dashboard. You do not need to send any configuration in the request.
Batch Processing
To process multiple images of the same form in one request, pass a comma-separated list of URLs in the
image_url field. Results are returned as an array in the same order.
Request
curl --location 'https://formread.org/api/forms/123/process' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{"image_url": "https://example.com/a.jpg,https://example.com/b.jpg,https://example.com/c.jpg"}'
Response
{
"results": [
{
"id": "0",
"image_url": "https://example.com/a.jpg",
"success": true,
"results": { "OMR-0-0": "A", "OCR-1": "John Smith" },
"anchor_error": false,
"processing_time_ms": 430
},
{
"id": "1",
"image_url": "https://example.com/b.jpg",
"success": true,
"results": { "OMR-0-0": "B", "OCR-1": "Jane Doe" }
},
{
"id": "2",
"image_url": "https://example.com/c.jpg",
"success": false,
"error": "Failed to fetch image from URL: 404"
}
],
"total": 3
}
Batch Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
image_url |
string | — | One or more public image URLs. Comma-separated for batch mode. |
details |
boolean | false | Include omr_details (per-bubble positions and fill ratios) in each batch result. Omitted by default to keep responses small. |
- Batch mode is only available with
image_url. File uploads andimage_base64are single-image only. - A failed URL does not fail the whole batch — that item gets
"success": falseand anerrorfield; the rest continue. - Results preserve the submitted order via
id(0-indexed). omr_detailsis always returned in single-image mode; in batch mode it requires"details": true.
Batch with omr_details
curl --location 'https://formread.org/api/forms/123/process' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"image_url": "https://example.com/a.jpg,https://example.com/b.jpg",
"details": true
}'
Limits
| Limit | Value | Behavior |
|---|---|---|
| Requests per minute | 60 |
Per API token. Exceeding returns HTTP 429. |
| Per-request timeout | 60s |
Requests exceeding 60 seconds are terminated with an error. |
A 429 response looks like this:
{
"message": "Too Many Attempts."
}
A batched request counts as one call regardless of how many URLs it contains.
- Per batch: measure
processing_time_mson a sample single-image response, then pack a batch that comfortably finishes under 60 seconds. - Across batches: pace requests so you stay under 60 per minute.
- Image size: downsize client-side to the smallest dimensions that still yield correct results. This is the biggest lever on per-image time.
Supported Area Types
| Type | Description | Result Format |
|---|---|---|
| OMR | Optical Mark Recognition — detects filled bubbles on answer sheets | "A", "B,C" |
| OCR | Optical Character Recognition — extracts printed or handwritten text | "John Smith" |
| BCR | Barcode/QR Code Recognition — reads barcodes and QR codes | "1234567890" |
Code Examples
Here are complete examples in Python and JavaScript for processing a form image:
Python
import requests
API_URL = "https://formread.org/api/forms/{form_id}/process"
TOKEN = "YOUR_API_TOKEN"
with open("scanned-form.jpg", "rb") as f:
response = requests.post(
API_URL.format(form_id=123),
headers={
"Accept": "application/json",
"Authorization": f"Bearer {TOKEN}",
},
files={"image": f},
)
data = response.json()
print(data["results"])
# {"OMR-0-0": "A", "OMR-0-1": "C", "OCR-1": "John Smith"}
Node.js
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const form = new FormData();
form.append('image', fs.createReadStream('scanned-form.jpg'));
const response = await axios.post(
'https://formread.org/api/forms/123/process',
form,
{
headers: {
...form.getHeaders(),
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_API_TOKEN',
},
}
);
console.log(response.data.results);
// { 'OMR-0-0': 'A', 'OMR-0-1': 'C', 'OCR-1': 'John Smith' }
Frequently Asked Questions
What does the Server-Side Processing API do?
You send a scanned form image in a single HTTP POST and receive structured OMR (bubble), OCR (text), and BCR (barcode/QR) results back as JSON — no browser, no iframe required.
How do I authenticate requests?
Pass your personal access token as a Bearer token in the Authorization header: Authorization: Bearer YOUR_API_TOKEN. Generate the token from the API Tokens section of your FormRead dashboard.
Can I process multiple images in a single request?
Yes. Pass a comma-separated list of public image URLs in the image_url field. Results come back in an array matching the submission order, and a failed URL only fails its own item — the rest continue.
What are the rate and size limits?
Up to 60 requests per minute per API token, with a 60-second processing timeout per request. A batched request counts as a single call regardless of how many URLs it contains.
Do I need to send the form configuration with every request?
No. The form configuration — read areas, thresholds, anchors — is loaded automatically from the form you set up in the FormRead dashboard. Your request only needs the image and the form id in the URL path.
Ready to Try FormRead?
Create, read, and process OMR forms with ease. Start extracting data from your forms today!
No credit card required to get started