Convert any image into clean text and markdown using advanced OCR. Send a photo, screenshot, scan, bill, or signage and get the text back instantly — with automatic language detection across 30+ languages.
Image OCR is a powerful API that extracts text and structured markdown from any image using advanced OCR — photos, screenshots, scanned documents, receipts, signage, handwriting, and multi-column layouts.
High-accuracy text recognition for photos, screenshots, and low-quality scans — even noisy or skewed images.
Leave lang blank and the engine detects the script automatically — no need to know the language upfront.
English, Hindi, Arabic, Chinese, Japanese, Spanish, French, and more — including multilingual combinations.
Get both plain text and structured markdown — paragraphs rebuilt from wrapped lines, caps lines promoted to headings.
Fully synchronous — no job ID, no polling, no webhooks. Send an image, get the text back in the same response.
Upload directly, send a public image URL, a Google Drive link, an extensionless image URL, or base64 data.
Every response includes character, word, and line counts, detected script + confidence, and processing time.
Simple REST API, flexible input formats, clear error messages, and consistent JSON responses.
Start free and scale as you grow. Every plan includes the full OCR engine with text + markdown output — higher tiers unlock larger images and more monthly extractions.
Everything you need to integrate the Image OCR API
All API requests require authentication using your API key. Send it via the x-api-key header with every request.
x-api-key: your_api_key_hereThe API exposes a single synchronous endpoint. Send one image, get the extracted text and markdown back in the same response — there is no job ID and no polling.
job_id, no status checks. Only successful extractions consume monthly quota.The API is synchronous and processes exactly one image per request. The full lifecycle happens inside a single HTTP call.
Send a POST request to /image-ocr/v1 with the image (file upload, URL, or base64) and an optional lang. The API reads the image, detects its MIME type, and measures its size.
Your API key, subscription, rate limit, and image size are checked against your plan. If lang is provided but unsupported, the request is rejected immediately — before any processing or quota check.
The OCR engine reads the image. If no lang was given, the script is auto-detected (Latin → eng, Devanagari → hin, and so on) and the matching language is applied automatically.
The response contains the plain text, the rebuilt markdown, character/word/line metrics, detected script, and processing time — all in one JSON body. One unit is deducted from your monthly quota only when the extraction succeeds.
The API accepts the image in any of these formats. Use whichever fits your environment.
Upload the image as a multipart form field. The field name can be image, file, data, photo, img, upload, or attachment — all are accepted. Any common format works: JPG, PNG, WebP, GIF, BMP, TIFF, HEIC, AVIF.
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-F "[email protected]"image_urlPass any public URL that returns image bytes. The image is validated by its actual content (MIME type), not by file extension — so URLs without an extension work too. For example, https://example.com/image that directly serves an image is accepted.
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d '{"image_url":"https://cdn.example.com/receipt.png"}'image_urlPaste a Google Drive share link directly. The API automatically detects Drive links (/file/d/ID/view, open?id=, uc?id=) and fetches the file. Make sure the file is shared with "Anyone with the link can view." Dropbox links are also normalized to direct download.
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d '{"image_url":"https://drive.google.com/file/d/1--EMd70IQHnLuVJXqXH...../view"}'image_base64Send the image inline as base64. Useful when your client can't perform multipart uploads (some no-code platforms). A data:image/png;base64,... prefix is accepted and stripped automatically.
IMG_B64=$(base64 -w0 photo.jpg)
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d "{\"image_base64\":\"$IMG_B64\"}"Content-Type header — form-data, JSON, and query strings all work. The image is auto-detected from whatever field you send it in, and URLs are validated by sniffing the downloaded bytes rather than the extension.| Header | Type | Description |
|---|---|---|
| x-api-keyRequired | String | Your API key. Send in request header. |
| Field | Type | Description |
|---|---|---|
| image / file / photoMultipart | File | Image file uploaded as multipart form data. Any of these field names work. |
| image_urlJSON/Form | String | Public URL serving image bytes — including direct CDN links, extensionless image URLs, and Google Drive / Dropbox share URLs. |
| image_base64JSON/Form | String | Base64-encoded image data. Supports raw base64 or data:image/png;base64,... prefix. |
| Field | Type | Description |
|---|---|---|
| langOptional | String | OCR language code or combination. Leave blank for auto-detect (recommended). Combine with + (e.g. eng+hin). See language list. |
lang, the engine detects the script from the image and picks the matching language. Pass lang only when you want to force a specific language — for example a French document (fra) where Latin-script auto-detect would default to eng.lang value that is not in the supported list, the request is rejected with INVALID_INPUT before authentication and before any quota is touched.curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-F "[email protected]"const form = new FormData();
form.append('image', fileInput.files[0]);
const res = await fetch('https://api.corenexis.com/image-ocr/v1', {
method: 'POST',
headers: { 'x-api-key': 'your_api_key' },
body: form
});
const data = await res.json();
console.log(data.data.text);import requests
with open("photo.jpg", "rb") as f:
res = requests.post(
"https://api.corenexis.com/image-ocr/v1",
headers={"x-api-key": "your_api_key"},
files={"image": f}
)
data = res.json()
print(data["data"]["text"])$ch = curl_init("https://api.corenexis.com/image-ocr/v1");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["x-api-key: your_api_key"],
CURLOPT_POSTFIELDS => ["image" => new CURLFile("photo.jpg")],
]);
$resp = json_decode(curl_exec($ch), true);
echo $resp["data"]["text"];curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-F "[email protected]" \
-F "lang=hin"curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-F "[email protected]" \
-F "lang=eng+hin"import requests
with open("document.jpg", "rb") as f:
res = requests.post(
"https://api.corenexis.com/image-ocr/v1",
headers={"x-api-key": "your_api_key"},
files={"image": f},
data={"lang": "fra"}
)
print(res.json()["data"]["markdown"])curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d '{"image_url":"https://cdn.example.com/receipt.png","lang":""}'# URL has no .png/.jpg — detected by content, not extension
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d '{"image_url":"https://example.com/image"}'curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-d '{"image_url":"https://drive.google.com/file/d/1--EMd70IQHnLuVJXqXH...../view","lang":"eng+hin"}'curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-F "image_url=https://cdn.example.com/screenshot.png"IMG_B64=$(base64 -w0 photo.jpg)
curl -X POST "https://api.corenexis.com/image-ocr/v1" \
-H "x-api-key: your_api_key" \
-H "Content-Type: application/json" \
-d "{\"image_base64\":\"$IMG_B64\"}"import requests, base64
with open("photo.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
res = requests.post(
"https://api.corenexis.com/image-ocr/v1",
headers={"x-api-key": "your_api_key"},
json={"image_base64": b64}
)
print(res.json()["data"]["text"])import requests
API_KEY = "your_api_key"
with open("photo.jpg", "rb") as f:
res = requests.post(
"https://api.corenexis.com/image-ocr/v1",
headers={"x-api-key": API_KEY},
files={"image": f}
).json()
if not res["success"]:
raise SystemExit(res["message"])
d = res["data"]
print("Detected lang:", d["lang"], "| auto:", d["lang_auto_detected"])
print("Words:", d["metrics"]["word_count"])
print("---- TEXT ----")
print(d["text"])
print("---- MARKDOWN ----")
print(d["markdown"])
print("Remaining quota:", res["usage"]["remaining"])const API_KEY = 'your_api_key';
const form = new FormData();
form.append('image', fileInput.files[0]);
const res = await fetch('https://api.corenexis.com/image-ocr/v1', {
method: 'POST',
headers: { 'x-api-key': API_KEY },
body: form
}).then(r => r.json());
if (!res.success) throw new Error(res.message);
const d = res.data;
console.log('Detected lang:', d.lang, '| auto:', d.lang_auto_detected);
console.log('Words:', d.metrics.word_count);
console.log(d.text);
console.log(d.markdown);
console.log('Remaining:', res.usage.remaining);Because the API is synchronous, the extracted text comes back in the same response — there is no status to poll. A successful call returns data.status = "completed" with the full OCR output.
{
"success": true,
"plan": "starter",
"data": {
"status": "completed",
"filename": "photo.jpg",
"file_size": "242.5 KB",
"image": { "width": 1280, "height": 720, "format": "jpeg" },
"lang": "eng",
"lang_auto_detected": true,
"metrics": {
"char_count": 1342,
"char_count_no_spaces": 1098,
"word_count": 233,
"line_count": 41
},
"text": "Plain OCR text exactly as read...",
"markdown": "## HEADING\n\nParagraph text rebuilt from wrapped lines..."
},
"usage": {
"remaining": 498,
"rate_limit": 20,
"monthly_limit": 500
}
}| Field | Type | Description |
|---|---|---|
| success | Boolean | true when the image was processed successfully |
| plan | String | Your current plan slug (free, starter, pro, max) |
| data.status | String | Always completed on success |
| data.filename | String | Original uploaded / resolved filename |
| data.file_size | String | Human-readable image size (e.g. 242.5 KB) |
| data.image | Object | Detected width, height, and format |
| data.lang | String | Language actually used for OCR |
| data.lang_auto_detected | Boolean | true if auto-detected, false if you passed lang |
| data.metrics.char_count | Integer | Total characters (with spaces) |
| data.metrics.word_count | Integer | Word count |
| data.metrics.line_count | Integer | Line count |
| data.text | String | Plain OCR text, as-is |
| data.markdown | String | Markdown version — paragraphs rebuilt, caps lines promoted to headings |
| usage.remaining | Integer | Extractions remaining this billing period |
| usage.rate_limit | Integer | Max requests per minute on your plan |
| usage.monthly_limit | Integer | Total monthly extractions on your plan |
Leave lang blank for automatic detection. To force a language, pass its code. Combine multiple codes with + for multilingual images (e.g. eng+hin, chi_sim+eng). Multilingual and non-English codes require Starter plan or higher.
| Code | Language | Code | Language |
|---|---|---|---|
eng | English | hin | Hindi |
ara | Arabic | fra | French |
deu | German | spa | Spanish |
por | Portuguese | ita | Italian |
rus | Russian | chi_sim | Chinese (Simplified) |
chi_tra | Chinese (Traditional) | jpn | Japanese |
kor | Korean | ben | Bengali |
urd | Urdu | tam | Tamil |
tel | Telugu | mar | Marathi |
guj | Gujarati | kan | Kannada |
mal | Malayalam | pan | Punjabi |
nld | Dutch | pol | Polish |
tur | Turkish | vie | Vietnamese |
tha | Thai | ind | Indonesian |
fas | Persian (Farsi) | heb | Hebrew |
The image's script is detected, then the matching language is applied automatically:
| Detected Script | Language used |
|---|---|
| Latin | eng |
| Devanagari | hin |
| Arabic | ara |
| Han | chi_sim |
| Japanese | jpn |
| Korean | kor |
| Cyrillic | rus |
| Bengali / Tamil / Telugu / etc. | respective code |
eng. For a French or German image, pass lang=fra or lang=deu for better accuracy. Non-Latin scripts auto-detect correctly.Every error response is consistent JSON: { success: false, code: "...", message: "..." }. Many errors include extra fields (param, provided, max_allowed) to help you self-correct without contacting support.
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Bad input — missing image, unsupported language code, or a file that is not a valid image. |
| 400 | PARAM_LIMIT_EXCEEDED | Request exceeds your plan's limit (image size or a feature). Includes param + max_allowed fields. |
| 400 | INVALID_BODY | Request body is not valid JSON. |
| 401 | MISSING_KEY | x-api-key header not present. |
| 401 | INVALID_KEY | API key is invalid, expired, or not found. |
| 403 | KEY_DISABLED | API key has been disabled. Generate a new one. |
| 403 | ACCOUNT_SUSPENDED | Account suspended. Contact support. |
| 403 | ACCOUNT_INACTIVE | Account not activated. Verify your email first. |
| 403 | EMAIL_NOT_VERIFIED | Email address has not been verified. |
| 402 | NO_SUBSCRIPTION | No active subscription on the Image OCR API. Subscribe first. |
| 402 | SUBSCRIPTION_EXPIRED | Subscription expired. Renew to continue. |
| 402 | SUBSCRIPTION_CANCELLED | Subscription cancelled. |
| 402 | SUBSCRIPTION_INACTIVE | Subscription is inactive. |
| 402 | BILLING_REQUIRES_ACTION | Billing requires action. Update payment method. |
| 404 | API_NOT_FOUND | API slug not configured on the platform. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests per minute. Wait 60 seconds. |
| 429 | QUOTA_EXCEEDED | Monthly quota reached. Upgrade your plan or wait for renewal. |
| 502 | PROCESSING_FAILED | Internal OCR service rejected or failed the request. Quota was not used. |
| 503 | API_UNAVAILABLE | Service temporarily unavailable. Retry shortly. |
| 503 | AUTH_SERVICE_UNAVAILABLE | Authentication backend is temporarily unreachable. Retry. |
{
"success": false,
"code": "PARAM_LIMIT_EXCEEDED",
"message": "Invalid value for 'max_file_size_bytes'. Allowed for your plan: 2097152 or less. Your request contains: 5242880. Please change 'max_file_size_bytes' or upgrade your plan.",
"param": "max_file_size_bytes",
"provided": 5242880,
"op": "lte",
"max_allowed": 2097152
}{
"success": false,
"code": "PARAM_LIMIT_EXCEEDED",
"message": "Feature 'lang' is not allowed on your current plan. Allowed for your plan: false. Your request contains: true. Please disable 'lang' or upgrade your plan.",
"param": "lang",
"provided": true,
"allowed": false
}{
"success": false,
"code": "INVALID_INPUT",
"message": "Unsupported language code 'klingon'. See the docs for the supported list."
}{
"success": false,
"code": "INVALID_INPUT",
"message": "The provided file is not a valid image.",
"detected_mime": "application/pdf"
}{
"success": false,
"code": "QUOTA_EXCEEDED",
"message": "Monthly quota reached. Upgrade your plan or wait for the next billing cycle."
}{
"success": false,
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please wait before making another request."
}Create your free account and start converting images to text and markdown in seconds. No credit card required.