The parse API
The analysis behind the parse page over HTTP, as Markdown, JSON, JSON Lines, YAML or CoNLL-U: sentences with their language, dialect and style, Universal Dependencies tokens (lemma, UPOS, XPOS, features, head, relation), the CoNLL-U document, and for English the frames (who did what to whom) and the pragmatics (speech act, form, indirectness, hedging, polarity, voice, implicatures, presuppositions, irony candidates). The analysis is deterministic: the same text, language and model versions give the same bytes. English is the language of the API; French is experimental (trees only).
This service runs the experimental Mova 0.6 pre-release; the stable release is 0.5 on GitHub. Using the API means agreeing to the Terms of use: the texts and the IP address are collected automatically.
The API lives at https://api.mova.elamur.ai (/v1/…). The same handlers also answer under /api/v1/… on mova.elamur.ai.
Formats
Choose with format= (a query parameter, or "format" in a JSON body), a suffix on a stored result's address, or the Accept header — in that order. Without any of them the answer is JSON.
| format | suffix | Accept | content |
|---|---|---|---|
md | .md | text/markdown | title, per sentence language/dialect/style, token table, tree outline, frames, pragmatics, model SHA-256s, CoNLL-U in a fence |
json | .json | application/json | the full answer with quota |
jsonl | .jsonl | application/jsonl, application/x-ndjson | one JSON object per sentence |
yaml | .yaml | application/yaml | the JSON answer as YAML, keys in order |
conllu | .conllu | text/plain | the CoNLL-U document |
GET https://api.mova.elamur.ai/v1/parse
Parameters: text (up to 2000 characters in an address), lang: auto (default), en or fr, format.
curl 'https://api.mova.elamur.ai/v1/parse?text=Can+you+pass+the+salt%3F&format=md' curl -H 'Accept: text/markdown' 'https://api.mova.elamur.ai/v1/parse?text=Can+you+pass+the+salt%3F'
POST https://api.mova.elamur.ai/v1/parse
A JSON body {"text": "…", "lang": "auto", "format": "json"}, or plain text with ?lang= and ?format= in the address. With an API key of an account (made on the account page), the account's limits apply.
curl -X POST https://api.mova.elamur.ai/v1/parse \
-H 'Content-Type: application/json' \
-d '{"text": "Some of the guests left early. They were tired.", "lang": "en"}'
curl -X POST 'https://api.mova.elamur.ai/v1/parse?lang=en&format=yaml' \
-H 'Authorization: Bearer elmr_…' \
-H 'Content-Type: text/plain' --data-binary @story.txt
GET https://api.mova.elamur.ai/v1/r/<id>.md
A stored result by its short id: the result page of a text too long for an address has the short link https://mova.elamur.ai/free/en/parse/s/<id>, and the same analysis is at https://api.mova.elamur.ai/v1/r/<id>.md (also .json, .jsonl, .yaml, .conllu). The same quotas, terms and request log apply as for a parse.
curl https://api.mova.elamur.ai/v1/r/0123456789ab.md
The JSON answer
{
"quota": {"plan": "anonymous", "per_request": 5000, "per_day": 250000,
"used_today": 5, "remaining_today": 249995, "resets_in_s": 40000, "words": 5},
"text": "Can you pass the salt?", the normalised text that was parsed
"lang_requested": "auto",
"sentences": [{
"id": 1, "text": "…", "lang": "en",
"dialect": {"label": null, "confidence": 0.0, "markers": []},
"style": null,
"spans": [{"start": 0, "end": 22, "lang": "en"}],
"parse": {"en": "en.bin"},
"tokens": [{"id": 1, "form": "Can", "lemma": "can", "upos": "AUX", "xpos": "MD",
"feats": "VerbForm=Fin", "head": 3, "deprel": "aux", "misc": "_", "lang": "en"}, …],
"parsed": true,
"frames": [{"kind": "event", "verb": "pass", "agent": "you", "patient": "the salt", …}],
"pragmatics": {"labels": {"act": {"value": "ask", "margin": 18.25}, "form": …, "indirect": …},
"cues": [], "implicatures": [], "presuppositions": [], "maxims": […], "irony": null}
}],
"conllu": "# sent_id = s-1\n# text = Can you pass the salt?\n…",
"versions": {"service": "mova-serve/1", "release": "0.6 pre-release", "stable": "0.5", "models": {"en": {"file": "en.bin", "sha256": "…"}}, …}
}Headers: X-Cache (hit or miss), X-Words (the words of this text), X-Quota-Plan, X-Quota-Limit (words a day), X-Quota-Remaining and X-Quota-Reset (seconds to 00:00 UTC). Everything but quota is the same for the same text; Markdown, JSONL and CoNLL-U carry the quota in these headers only (YAML has it like JSON).
MCP: the parse service for AI assistants
The MCP page has the details and client configs. An MCP server (Model Context Protocol, revisions 2025-03-26 to 2025-11-25) with two read-only tools: parse_text (the analysis; fields narrows it to tokens, conllu, frames, pragmatics) and explain_sentence (a sentence's grammar, frames and speech act in plain English). Both return Markdown text by default; the argument format (md, json, jsonl, yaml, conllu) chooses another, and json also fills structuredContent. It needs an API key of an account; the account's quota applies.
Streamable HTTP, for remote clients:
https://api.mova.elamur.ai/v1/mcp Authorization: Bearer elmr_…
curl -X POST https://api.mova.elamur.ai/v1/mcp \
-H 'Authorization: Bearer elmr_…' -H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"explain_sentence","arguments":{"text":"Can you pass the salt?"}}}'stdio, for local clients: the mova-serve binary from the repository forwards each message to the HTTP endpoint with your key:
{"mcpServers": {"mova": {"command": "mova-serve",
"args": ["mcp-stdio", "--endpoint", "https://api.mova.elamur.ai/v1/mcp"],
"env": {"ELAMUR_API_KEY": "elmr_…"}}}}
GET https://api.mova.elamur.ai/v1/health
Whether the service is up, its release and model versions, the cache's entries and hits, the queue and aggregate request counts.
curl https://api.mova.elamur.ai/v1/health
Limits
Words are counted by Mova's tokenizer. Anonymous use: 5,000 words a request and 250,000 words a day per IP address; a free account (API key or signed in): 20,000 words a request and 1,000,000 a day. A text in an address: up to 2000 characters; a body: up to 2 MB. Against bursts each client has a bucket of 40 requests refilled at 1 a second. 4 parses run at once and 16 wait; a parse gives up after 60 seconds.
Status codes: 400 a bad parameter or format, 401 a missing or unknown key (MCP), 404 an unknown or expired short id, 413 a text longer than the plan allows, 429 the day's words used up or too many requests at once (see Retry-After), 503 busy (try again in a few seconds), 504 the parse took too long.
Terms of use · Privacy policy · Parse · API · MCP · Account