# ChronoEye API ChronoEye reads scanned documents (OCR) and compresses them with neural MRC (Mixed Raster Content) layers. This is the **trial service**: free for 30 days after you create your key. - **Base URL:** `https://cs-miniserver.swedencentral.cloudapp.azure.com` - **Get a key:** [/chronoeye/account/](/chronoeye/account/) (free, 30 days) - **Web tools, no code:** [/chronoeye/](/chronoeye/) - **This page as Markdown:** [/chronoeye/API.md](/chronoeye/API.md) | Method | Path | What it does | |---|---|---| | POST | `/v1/images:annotate` | OCR one image. Google Cloud Vision-compatible request and response. | | POST | `/v1/documents:searchable` | PDF or image in, compressed PDF with an invisible text layer out. | | POST | `/v1/documents:compress` | PDF or image in, MRC-compressed PDF out. Text in PDFs is kept. | | POST | `/v1/jobs?op=searchable\|compress` | Same two operations, asynchronous: returns a job id straight away. | | GET | `/v1/jobs` | Your latest jobs (history). | | GET | `/v1/jobs/{id}` | Status and progress of one job. | | GET | `/v1/jobs/{id}/result` | Download the output (PDF, or JSON for OCR). | | GET | `/v1/jobs/{id}/text` | The recognized text as plain UTF-8 (searchable PDFs and OCR calls). | | GET | `/v1/jobs/{id}/input` | Download the file you sent. | | DELETE | `/v1/jobs/{id}` | Delete both files from the history now. | | GET | `/api/ce/status` | Is the service online? No key needed. | | GET | `/api/health`, `/api/status` | Service probe, same as a ChronoEye station: `"models_loaded": true` when OCR is available. No key needed. | --- ## Authentication Every `/v1/*` call needs your API key (`ce_` followed by 40 hex characters). Send it in any of these ways: | Where | Example | |---|---| | Query string (Google style) | `/v1/images:annotate?key=ce_…` | | Header | `X-Api-Key: ce_…` (also `X-Goog-Api-Key`) | | Bearer token | `Authorization: Bearer ce_…` | Keep the key secret. If it leaks, create a new one on the account page: the old one stops working at once. ## Limits (trial) | Limit | Value | |---|---| | Trial length | 30 days from when you create your first key | | Pages per day | 200 (resets at midnight, Europe/Madrid) | | Pages per document | 50 | | Request size | 60 MB | | History | Inputs and outputs are kept for 30 days, then deleted | A page is one image, one frame of a multi-page TIFF, or one page of a PDF. Only successful work counts. ## Errors Errors are JSON: `{"error": {"code": 429, "message": "…"}}`. | Code | Meaning | |---|---| | 400 | The file is not a supported PDF or image, or the request is malformed | | 401 | Missing or unknown API key | | 403 | The trial ended, or the account is disabled | | 413 | The document has more pages than allowed per document | | 429 | Daily page limit reached | | 502 | Processing failed on our side (you are not charged pages) | | 503 | ChronoEye is offline. The processing machine is not reachable; try again later | --- ## POST /v1/images:annotate OCR of a single image, in the **Google Cloud Vision** format. If your software already talks to Vision (ChronoScan does), point it at this host and use your ChronoEye key as the API key. Two request forms: **A. Raw image body.** Send the file with `Content-Type: image/jpeg`, `image/png`, `image/tiff`, etc. ```bash curl -X POST "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/images:annotate?key=ce_…" \ -H "Content-Type: image/jpeg" --data-binary @scan.jpg ``` **B. Google-style JSON.** ```bash curl -X POST "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/images:annotate?key=ce_…" \ -H "Content-Type: application/json" \ -d "{\"requests\":[{\"image\":{\"content\":\"$(base64 -w0 scan.jpg)\"}}]}" ``` Only the first entry of `requests` is processed, and the service always performs document text detection: feature lists are ignored. PDFs are not accepted here; use `/v1/documents:searchable`. Optional query parameter: `auto_orient=0` skips automatic page orientation. ### Response Google Cloud Vision structure, with PascalCase field names. Abbreviated: ```jsonc { "Responses": [{ "FullTextAnnotation": { "Pages": [{ "Width": 2480, "Height": 3508, // source image size in px "Blocks": [{ "BoundingBox": { "Vertices": [ {"X":0,"Y":0}, … ] }, "Paragraphs": [{ // one paragraph per text line "Words": [{ "BoundingBox": { "Vertices": [ // rotated quad, integer px {"X":102,"Y":88}, {"X":215,"Y":88}, {"X":215,"Y":121}, {"X":102,"Y":121} ] }, "Symbols": [{ "Text": "F", "Confidence": 0.98 }, … ], "Confidence": 0.98 }, … ] }, … ], "BlockType": 1 }] }], "Text": "First line\nSecond line\n" // full plain text } }] } ``` - `DetectedBreak.Type` on a word's last symbol: `1` = space, `5` = end of line (Google `BreakType` values). - Word `Confidence` (0 to 1) comes from the recognizer. Vertices are integers and may describe rotated boxes. - Words that were physically rotated before reading carry `"Rotation": 90|180|270`. - Response headers: `X-Ce-Job` (the history id of this request) and `X-Ce-Pages`. --- ## POST /v1/documents:searchable Send a scanned **PDF** or an **image** (JPEG, PNG, TIFF including multi-page, BMP) as the raw body. You get back a PDF where every page is MRC-compressed and carries an invisible text layer, so it can be searched, selected and copied. Page sizes are preserved. ```bash curl -X POST "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/documents:searchable?key=ce_…&level=5" \ -H "Content-Type: application/pdf" --data-binary @scan.pdf -o scan.searchable.pdf ``` | Query parameter | Default | Meaning | |---|---|---| | `level` | `5` | Compression, `1` (best quality) to `10` (smallest file) | | `filename` | – | Name shown in your history | The call waits until the PDF is ready: about 5 to 10 seconds per page. For long documents, prefer the asynchronous `/v1/jobs`. ## POST /v1/documents:compress Same input, but no OCR: **PDFs are recompressed in place**. Full-page scans get the neural MRC treatment, photos are re-encoded, and all existing text, vectors and OCR layers are kept. Images become an MRC PDF without a text layer. Typical result for scanned PDFs: 10 to 15 times smaller. ```bash curl -X POST "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/documents:compress?key=ce_…&level=6" \ -H "Content-Type: application/pdf" --data-binary @big.pdf -o small.pdf ``` Parameters: `level`, `filename`, as above. --- ## Asynchronous jobs For large documents, or to show progress in your own UI. **1. Submit.** Body = the file, as in the calls above. ```bash curl -X POST "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/jobs?op=searchable&level=5&filename=contract.pdf" \ -H "X-Api-Key: ce_…" -H "Content-Type: application/pdf" --data-binary @contract.pdf ``` Returns `202`: ```json { "id": "5f0c…e1", "op": "searchable", "status": "queued", "filename": "contract.pdf", "pages": 12, "progress": 0, "error": null, "note": null, "bytes_in": 7340032, "bytes_out": 0, "ms": 0, "created": "2026-09-29T10:15:00Z", "result_url": null, "input_url": "/v1/jobs/5f0c…e1/input" } ``` **2. Poll** `GET /v1/jobs/{id}` about once a second. `status` goes `queued` → `running` (with `progress` 0 to 100) → `done` or `error`. When it is `done`, `result_url` is set. `note` may carry a warning, for example when no text was found. **3. Download** `GET /v1/jobs/{id}/result`. For `searchable` jobs, `GET /v1/jobs/{id}/text` (also in `text_url`) returns the recognized text as plain UTF-8, with `--- Page N ---` separators when there is more than one page. Jobs run one at a time, in order of arrival. ## History `GET /v1/jobs?limit=50` lists your latest requests, newest first, including `images:annotate` calls (their result is the OCR JSON). Files stay available for 30 days; after that, or after `DELETE /v1/jobs/{id}`, the downloads answer `410`. --- ## Examples ### Python ```python import requests BASE = "https://cs-miniserver.swedencentral.cloudapp.azure.com" KEY = "ce_…" # read it from an environment variable in real code # OCR (Vision format) with open("scan.jpg", "rb") as f: r = requests.post(f"{BASE}/v1/images:annotate", params={"key": KEY}, headers={"Content-Type": "image/jpeg"}, data=f) r.raise_for_status() print(r.json()["Responses"][0]["FullTextAnnotation"]["Text"]) # Searchable PDF with open("scan.pdf", "rb") as f: r = requests.post(f"{BASE}/v1/documents:searchable", params={"key": KEY, "level": 5}, headers={"Content-Type": "application/pdf"}, data=f, timeout=600) r.raise_for_status() open("scan.searchable.pdf", "wb").write(r.content) ``` ### PowerShell ```powershell $base = "https://cs-miniserver.swedencentral.cloudapp.azure.com" $key = $env:CHRONOEYE_KEY Invoke-RestMethod -Method Post -Uri "$base/v1/documents:compress?key=$key&level=6" ` -ContentType "application/pdf" -InFile big.pdf -OutFile small.pdf ``` ### C# ```csharp using var http = new HttpClient { Timeout = TimeSpan.FromMinutes(10) }; http.DefaultRequestHeaders.Add("X-Api-Key", Environment.GetEnvironmentVariable("CHRONOEYE_KEY")); var body = new ByteArrayContent(await File.ReadAllBytesAsync("scan.jpg")); body.Headers.ContentType = new("image/jpeg"); var res = await http.PostAsync( "https://cs-miniserver.swedencentral.cloudapp.azure.com/v1/images:annotate", body); res.EnsureSuccessStatusCode(); Console.WriteLine(await res.Content.ReadAsStringAsync()); ``` --- ## Privacy Files are processed on our own hardware, not on a third-party cloud. We keep what you send and what we return for 30 days so you can download it again from your history, then delete it. You can delete any item sooner from the API (`DELETE /v1/jobs/{id}`). This is a trial service with no uptime guarantee.