Skip to content

API Endpoints

Base URL: https://biomapi.com

Interactive docs (Swagger UI): /apidocs


Process a biometry file and return structured measurements.

The engine is selected automatically based on the file extension — no manual selection needed:

ExtensionEngineWhat happens
PDF, JPG, PNG, GIF, BMPBiomAIBytes sent to Gemini LLM for structured extraction
JSONBiomJSONSchema validation + metadata preservation

Unsupported extensions are rejected with 400 before any processing occurs.

Multipart form data:

FieldTypeRequiredDefaultDescription
filefileYesBiometry report file
biompinboolNofalseGenerate a BiomPIN for secure sharing

Headers:

HeaderDescription
Authorization: Bearer <key>Optional BiomAPI key — unlocks higher per-user rate limits
X-Gemini-API-Key: <key>Optional BYOK Gemini key — uses your own Google quota

BiomPIN is off by default (biompin=false). Set biompin=true to receive a secure sharing link in the response. If generation fails (e.g., DB unavailable), processing still succeeds and the biompin field is null.

When X-Gemini-API-Key is provided, extraction uses your personal Gemini quota instead of the shared server quota. The request is tracked under the biomai_byok rate limit bucket (separate from biomai). BYOK has no effect on JSON uploads — BiomJSON doesn’t call the LLM.

Rate limits are applied after a successful extraction. Files that fail validation or cause LLM errors do not count against your quota.

ScenarioBucketPublic default
PDF/image, no BYOKbiomai30/day
PDF/image, BYOKbiomai_byok1,000/day
JSON uploadbiomjson300/day

When you re-upload a JSON that originally came from a BiomAI extraction, BiomJSON preserves the original LLM metadata (token counts, model name, timing). The input_schema_version field is populated with the schema version of the uploaded JSON, so you can detect if it was produced by an older BiomAPI version. If the JSON has no recognizable BiomAI provenance, it’s attributed as BiomDIRECT.

The extra_data.posterior_keratometry field is populated only for devices that support PK extraction (Anterion, EyestarES900, IOLMaster700, MS39, PentacamAXL). For other devices, extra_data is null.

{
"data": {
"biometer": {
"device_name": "IOLMaster700",
"manufacturer": "Zeiss"
},
"patient": {
"name": "JD",
"id": "12345",
"date_of_birth": "1965-03-15",
"gender": "Male"
},
"right_eye": {
"AL": 23.45,
"ACD": 3.12,
"K1_magnitude": 43.25,
"K1_axis": 5,
"K2_magnitude": 44.50,
"K2_axis": 95,
"WTW": 11.8,
"LT": 4.52,
"CCT": 545,
"lens_status": "Phakic",
"post_refractive": "None",
"keratometric_index": 1.3375
},
"left_eye": { "...": "..." }
},
"extra_data": {
"notes": null,
"posterior_keratometry": {
"pk_device_name": "IOLMaster700",
"right_eye": { "PK1_magnitude": 6.12, "PK1_axis": 8, "PK2_magnitude": 6.45, "PK2_axis": 98 },
"left_eye": { "PK1_magnitude": 6.08, "PK1_axis": 172, "PK2_magnitude": 6.38, "PK2_axis": 82 }
}
},
"metadata": {
"schema_version": "1.0.0",
"app_version": "0.9.8.1",
"extraction": {
"method": "BiomAI",
"timestamp": "2025-01-15T10:30:00Z",
"filename": "report.pdf",
"byok": false,
"llm": "gemini-flash-latest",
"llm_api_metrics": {
"prompt_token_count": 1500,
"cached_content_token_count": null,
"candidates_token_count": 200,
"thoughts_token_count": null,
"total_token_count": 1700,
"cache_hit_ratio": null
},
"llm_performance": {
"llm_response_time_seconds": 2.5,
"retry_attempts": 0,
"total_retry_delay_seconds": 0.0
}
}
},
"biompin": {
"pin": "lunar-rocket-731904",
"expires_at": "2025-02-15T10:30:00Z",
"db_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}

See Response Schema for the complete field reference.

CodeCondition
400Empty file, unsupported extension, or invalid file content
422JSON schema mismatch (major version) or Pydantic validation failure
429Rate limit exceeded for the applicable engine bucket
504BiomAI extraction exceeded BIOMAI_TIMEOUT_SECONDS (default 30 s)

Retrieve encrypted biometry data using a BiomPIN code.

BiomPIN codes have two parts: word-word (the share ID, stored in the database) and -123456 (the 6-digit numeric PIN, the encryption key, never stored). The server derives the AES-256-GCM decryption key from the numeric PIN using Argon2id. Without the full PIN including the numeric suffix, the data cannot be decrypted.

Rate limiting for this endpoint is applied before retrieval (unlike /process). This prevents quota exhaustion via wrong PIN attempts, though the primary brute-force protection is the auto-destroy mechanism.

GET /api/v1/biom/retrieve?biom_pin=lunar-rocket-731904
ParameterRequiredDescription
biom_pinYesFull PIN including numeric suffix: word-word-123456

Each wrong numeric PIN increments a counter. After 3 failed attempts, the database record is permanently deleted — the data is gone, and all subsequent requests for that share ID return 404. This is intentional: it prevents offline brute-forcing by destroying the ciphertext.

Note that rate limiting is also applied per-IP/per-user, but the 3-attempt destruction is tracked in the database independently of rate limit state.

Same StandardAPIResponse shape as /process. The biompin field reflects the original PIN info (pin, expiry, db_id).

CodeCondition
400PIN doesn’t match the word-word-123456 format
404PIN not found, already expired, or destroyed after too many wrong attempts
429Rate limit exceeded

Generate a by-eye CSV from an array of StandardAPIResponse objects.

No authentication required. No rate limiting. Pure data transformation — no LLM calls.

The CSV produces two rows per report: one for the right eye (right_eye=1) and one for the left eye (right_eye=0). Each row includes patient info, device info, and all 12 biometric fields as flat columns.

{
"responses": [
{ "json_data": { "...": "StandardAPIResponse object" }, "filename": "report1.pdf" },
{ "json_data": { "...": "StandardAPIResponse object" }, "filename": "report2.json" }
]
}

text/csv file download (Content-Disposition: attachment; filename=biometry_export.csv).


Generate a ZIP archive containing a by-eye CSV and individual JSON files per report.

No authentication required. No rate limiting.

FileDescription
biomapi-export-YYYYMMDD-HHMMSS.csvBy-eye CSV of all results (same format as /csv)
biomapi-{patientId}-{device}.jsonOne JSON file per report

Smart filenames: JSON filenames are derived from data.patient.id (slugified) and data.biometer.device_name. If no patient ID is present, the current date is used instead. Duplicate filenames within the same archive are disambiguated with a numeric suffix.

Same structure as /csv: { "responses": [...] }.

application/zip file download.


Returns current rate limit usage and API key validity for the caller.

No rate limiting applied to this endpoint — it’s a read-only in-memory query.

Public callers see per-IP usage. Authenticated callers see per-user usage. The engines section shows used/limit/resets for all four engine buckets regardless of which ones have been used.

The keys section reports validity of the BiomAPI key (instant, in-memory check) and optionally the Gemini BYOK key.

Pass ?validate=true to test the X-Gemini-API-Key header against the live Gemini API. This adds ~1–2 seconds of latency. Without validate=true, the gemini key entry only reports "provided": true — no validity check is performed.

GET /api/v1/biom/usage
GET /api/v1/biom/usage?validate=true
Query parameterDefaultDescription
validatefalseAlso validate X-Gemini-API-Key against Gemini API (~1–2 s)
{
"auth_type": "authenticated",
"user_id": "user1",
"engines": {
"biomai": { "used": 5, "limit": 300, "resets_in_seconds": 72400 },
"biomai_byok": { "used": 0, "limit": 3000, "resets_in_seconds": 72400 },
"biomjson": { "used": 2, "limit": 3000, "resets_in_seconds": 72400 },
"retrieve": { "used": 12, "limit": 10000, "resets_in_seconds": 72400 }
},
"keys": {
"biomapi": { "provided": true, "valid": true, "user_id": "user1" },
"gemini": { "provided": true, "valid": true }
}
}

keys field details:

Scenariobiomapi valuegemini value
No keys provided{"provided": false}{"provided": false}
BiomAPI key provided, valid{"provided": true, "valid": true, "user_id": "..."}
BiomAPI key provided, invalid{"provided": true, "valid": false}
Gemini key provided, validate=false{"provided": true}
Gemini key provided, validate=true, valid{"provided": true, "valid": true}
Gemini key provided, validate=true, invalid{"provided": true, "valid": false}

resets_in_seconds is the time until the oldest tracked request in the sliding window rolls off — i.e., the earliest your limit could recover by one slot.


Health check. Public, no authentication required.

{
"status": "operational",
"gemini_api_connected": true,
"db_connected": true,
"db_records": 42,
"gemini_llm": "gemini-flash-latest",
"environment": "production",
"app_version": "0.9.8.1",
"schema_version": "1.0.0",
"db_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
FieldDescription
status"operational" (all healthy), "degraded" (one of Gemini/DB unavailable), "error" (both unavailable)
gemini_api_connectedWhether the server’s Gemini API key is valid and reachable
db_connectedWhether the BiomPIN SQLite database is healthy
db_recordsNumber of active (non-expired) BiomPIN records
gemini_llmThe Gemini model configured on the server
db_idPersistent deployment identifier — changes when the BiomPIN database is wiped

db_id is stable across server restarts but changes when the BiomPIN database is reset (e.g., via BIOMPIN_RESET_DB=true). If you maintain a local BiomPIN history, compare this value on startup to detect a database wipe:

const status = await fetch('/api/v1/status').then(r => r.json());
if (status.db_id !== storedDbId) {
// All saved PINs from the old instance are gone — clear local history
}

The BiomAPI web app does this automatically via the History tab.


RouteDescription
GET /Main web application (SPA)
GET /pin/{biompin}Direct BiomPIN access — pre-fills the BiomPIN tab and auto-retrieves
GET /docs/This documentation site
GET /apidocsSwagger UI (interactive API explorer)
GET /openapi.jsonOpenAPI 3.0 spec (machine-readable, powers Swagger UI)
GET /llms.txtLLM-readable site index (also /llms-full.txt, /llms-small.txt)