API reference

Send a page image, get its text back in reading order, as Markdown or plain text, in the original language and script. One endpoint, one key.

Preview The API serves preview models until Rune-1 launches. $5 free credit for new accounts.

Quick start

  1. Create a key

    Sign in to the Runemic Console with GitHub or Hugging Face and create a key under API keys. It is shown once.

  2. Store it

    Keep it out of your code: export RUNEMIC_API_KEY="rk_live_…"

  3. Send a page

    PNG, JPEG, WebP or GIF, up to 4 MB.

cURL

curl https://api.runemic.com/v1/ocr \
  -H "Authorization: Bearer $RUNEMIC_API_KEY" \
  -F "file=@page.png" \
  -F "model=preview-gemma-4"

Python

import os, requests

resp = requests.post(
    "https://api.runemic.com/v1/ocr",
    headers={"Authorization": f"Bearer {os.environ['RUNEMIC_API_KEY']}"},
    files={"file": open("page.png", "rb")},
    data={"model": "preview-gemma-4"},
)
resp.raise_for_status()
print(resp.json()["text"])

JavaScript (Node 18+)

import { readFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("page.png")], { type: "image/png" }), "page.png");
form.append("model", "preview-gemma-4");

const res = await fetch("https://api.runemic.com/v1/ocr", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.RUNEMIC_API_KEY}` },
  body: form,
});
console.log((await res.json()).text);

Authentication

Every request to /v1/ocr needs your key in the Authorization header: Authorization: Bearer rk_live_…. Keys can be renamed and revoked in the console; a revoked key stops working immediately. Never put a key in a website or app that other people can inspect; call the API from your server.

POST /v1/ocr

Reads one page. Send multipart/form-data with a file, or JSON with an image.

ParameterTypeDescription
filefileThe page image (multipart). PNG, JPEG, WebP or GIF, max 4 MB.
imagestringJSON alternative: a base64 data URI (data:image/png;base64,…) or a public https:// URL.
modelstringOptional. A model ID from GET /v1/models. Default preview-gemma-4.
formatstringOptional. markdown (default: headings, lists and tables) or text (plain text).
instructionsstringOptional, up to 300 characters of extra guidance, for example "Only return the table".
streambooleanOptional. true sends the text while the model writes it, as server-sent events (see below).

Streaming

A full page takes several seconds to write out. With stream set, the response is text/event-stream: delta events carry pieces of text as they are written, then one done event carries the full result, with the same fields as a normal response. If the model fails, you get an error event instead, and you are not charged.

curl -N https://api.runemic.com/v1/ocr \
  -H "Authorization: Bearer $RUNEMIC_API_KEY" \
  -F "file=@page.png" \
  -F "stream=true"

event: delta
data: {"text": "# Invoice No. 4821\n\nCity"}

event: done
data: {"id": "req_c28e0b7d9a4f13e65b02", "object": "ocr.result", "text": "# Invoice No. 4821 …", "usage": {…}}

JSON request

curl https://api.runemic.com/v1/ocr \
  -H "Authorization: Bearer $RUNEMIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image": "https://example.com/page.jpg", "format": "text"}'

Response

{
  "id": "req_7f3a9c2e41b8d05e6a17",
  "object": "ocr.result",
  "model": "preview-gemma-4",
  "text": "# INVOICE No. 4821\n\nCity Library …",
  "format": "markdown",
  "usage": { "pages": 1, "cost_usd": 0.002, "ms": 2210 },
  "balance_usd": 4.998
}

Every response also has an X-Request-Id header that matches the request in the console's Logs.

GET /v1/models

Lists the available models and their price per page. No key needed: curl https://api.runemic.com/v1/models

Model IDBased onSpeedBest forPer page
preview-gemma-4
default
Google Gemma 4 26BBalancedMultilingual pages, right-to-left scripts$0.002
preview-mistral-smallMistral Small 3.1 24BFastTables, forms, invoices$0.003
preview-llama-4-scoutMeta Llama 4 ScoutFastestClean printed pages, high volume$0.002

Preview models run on Cloudflare Workers AI. When Rune-1 launches it becomes a new model ID; your key and code stay the same.

OpenAI-compatible API

Already using an OpenAI SDK, or a tool built on one? Point it at https://api.runemic.com/v1 with your Runemic key and send the page as an image in a chat message. POST /v1/chat/completions, GET /v1/models and GET /v1/models/{id} follow OpenAI's format, including streaming and error types.

from openai import OpenAI
import base64, os

client = OpenAI(base_url="https://api.runemic.com/v1", api_key=os.environ["RUNEMIC_API_KEY"])
image = "data:image/png;base64," + base64.b64encode(open("page.png", "rb").read()).decode()

reply = client.chat.completions.create(
    model="preview-gemma-4",
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "Only return the table."},  # optional: how to transcribe
        {"type": "image_url", "image_url": {"url": image}},
    ]}],
)
print(reply.choices[0].message.content)
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.runemic.com/v1", apiKey: process.env.RUNEMIC_API_KEY });
const stream = await client.chat.completions.create({
  model: "preview-gemma-4",
  stream: true,
  messages: [{ role: "user", content: [{ type: "image_url", image_url: { url: "https://example.com/page.jpg" } }] }],
});
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
In the requestWhat Runemic does with it
messagesExactly one image, as an image_url part in a user message: a data: URI or a public https URL (PNG, JPEG, WebP or GIF, up to 4 MB). Text in user messages becomes transcription instructions (up to 300 characters). System and assistant messages are ignored.
modelA model ID from GET /v1/models. Leave it out to use the default with automatic fallback.
stream, stream_options.include_usageStreams chat.completion.chunk events and ends with data: [DONE], like OpenAI.
format (Runemic extension)markdown (default) or text. With the OpenAI SDKs, pass it as extra_body={"format": "text"} (Python) or add it to the request object (JavaScript).
temperature, max_tokens, tools and other chat optionsAccepted and ignored: the models only transcribe.

The answer is the page's text as the assistant message. usage counts tokens; the price is per page, as with /v1/ocr, and is shown in the extra runemic field with cost_usd and balance_usd. Same key, limits and billing as /v1/ocr.

Use with AI assistants (MCP)

Runemic is an MCP server, so Claude, ChatGPT, Codex, Cursor, VS Code and other assistants can read pages for you. The server address is https://mcp.runemic.com/mcp.

Add it to your assistant and it sends you to Runemic to sign in (GitHub or Hugging Face) and allow access. There is no key to copy. Pages are charged to your Runemic credit, with the same prices and limits as the API.

AssistantHow to add it
Claude (web, desktop, mobile)Settings → Connectors → Add custom connector. Name it Runemic, paste the address, then Connect.
Claude CodeRun the commands below, then /mcp and choose Authenticate.
Codex (CLI, app, IDE)Add the lines below; codex mcp login runemic opens the sign-in page.
ChatGPTWith developer mode on: Settings → Apps → Create app, paste the address and choose OAuth.
CursorAdd to Cursor, or put the JSON below in ~/.cursor/mcp.json.
VS CodeAdd to VS Code, or MCP: Add Server → HTTP and paste the address.
Anything elseUse the address with the Streamable HTTP transport. Clients that can't sign in can send Authorization: Bearer <your API key> instead.
# Claude Code: add the server, then run /mcp in Claude Code to sign in
claude mcp add --transport http runemic https://mcp.runemic.com/mcp

# or install the plugin (the server plus a skill for local files)
/plugin marketplace add runemic/runemic.github.io
/plugin install runemic@runemic
# Codex: ~/.codex/config.toml
[mcp_servers.runemic]
url = "https://mcp.runemic.com/mcp"

# then sign in once
codex mcp login runemic
{
  "mcpServers": {
    "runemic": { "type": "http", "url": "https://mcp.runemic.com/mcp" }
  }
}
ToolWhat it does
read_pageReads one page image, given as a public https URL or a data: URI (PNG, JPEG, WebP or GIF, up to 4 MB). Optional format, model and instructions, as in POST /v1/ocr. Returns the text, the model, the cost and your balance.
upload_pageShows a Runemic file picker in the chat (in assistants that support MCP Apps, such as Claude and ChatGPT). You choose page images from your device, even several at once, and their text is sent to the conversation. Images are read and not stored.
list_modelsThe models available now, with prices.
get_accountYour credit balance, today's pages and limit, and this month's usage.

Assistants can't pass an image you attach in a chat to a tool, so Runemic brings its own picker: ask the assistant to read a page and choose the file in the Runemic panel that appears. Where no panel can be shown, give a link instead; in Claude Code and Codex, a file path works too, because the plugin's skill sends the file with the OCR API. To see or disconnect connected apps, open the console and go to Settings → Connected apps.

Errors

Errors return JSON like {"error": {"code": "…", "message": "…"}}. Failed requests are never charged.

StatusCodeMeaning
400invalid_image · unknown_model · invalid_formatCheck the request body.
401invalid_api_keyMissing, wrong or revoked key.
413 · 415too_large · unsupported_typePNG, JPEG, WebP or GIF up to 4 MB. PDFs are coming soon.
429rate_limited · daily_limitToo many requests per minute, or today's page limit reached.
502model_errorThe model failed. You are not charged; retry or pick another model.
503capacityToday's shared preview capacity is used up. Try again after 00:00 UTC.
504model_timeoutThe model you chose didn't answer in time. You are not charged; retry or pick another model.

Rate limits

Requests per minute, per account (all keys together)20
Pages per day, per account (preview)500
Pages per day on an account's first day, or for GitHub accounts less than 14 days old50
Image size4 MB
Active keys per account10

Need more? Email hello@runemic.com.

Data and privacy

Images are processed by the selected model and returned to you. We never store your images or their text, and never use them for training. We keep request metadata (time, model, pages, cost, latency, status) for your usage and billing. Read the privacy notice.

Changelog

  • 1 Oct 2026: MCP server at https://mcp.runemic.com/mcp for Claude, ChatGPT, Codex, Cursor and VS Code, with sign-in, plus plugins for Claude Code and Codex.
  • 30 Sep 2026: OpenAI-compatible POST /v1/chat/completions. Without a model, another model takes over automatically if the default fails or doesn't answer; a chosen model that doesn't answer returns 504 model_timeout. Request IDs are now random (req_ + 20 characters).
  • 25 Sep 2026: stream parameter: get the text while it is being written. Removed preview-qwen-3.8.
  • 24 Sep 2026: public preview. Four preview models, format and instructions parameters, request IDs, console with playground and usage analytics.

Ready to try it?

New accounts get $5 of free credit. No card needed.