CTO
10.3.2026
Málstaður's product functionality is now accessible via API at https://api.malstadur.is. There you can access proofreading, speech recognition and translations, as well as additional functionality such as plain language and summarisation at a later date.
Til að fá aðgang að forritaskilum þarf fyrst að stofna til frjálsrar áskriftar. Þá geta eigendur og stjórnendur áskriftar nálgast síðu um forritaskil í notendavalmyndinni niðri til vinstri:
To get access to the API, you first need to create a frjáls áskrift subscription. This enables subscription owners and administrators to access the forritaskil API management page in the user menu in the bottom left:
There you can create API keys with specified permissions and validity periods, as well as monitor usage and costs. The services that the keys can be used for are:
Proofreading (/v1/grammar): The same proofreading that is available in Málfríður.
Speech recognition (/v1/asr): The same speech recognition that is available in Hreimur.
Translations (/v1/translate): The same translation functionality that is available in Erlendur.
Text transformation: At the moment, no endpoints for text transformation are active, but endpoints for summarisation and plain language, among others, will be added here.
General access to the API costs nothing beyond the general fee for a subscription, but usage is subject to the following charges:
1 kr. / 100 characters for proofreading, translations and text transformation.
2.000 kr. / hour for speech recognition.
Usage credit included with a subscription can only be used on the website and not on the API.
If you need greater access to the API, e.g. for formal operation of a product with fewer restrictions on the number of requests at any given time (rate limit), this can be provided with a specific service agreement.
Below are simple examples of how to use the main APIs in Python, all of which follow the setup below (assuming that httpx is installed in the environment):
import httpx
API_KEY = "YOUR_API_KEY"
BASE = "https://api.malstadur.is/v1"
HEADERS = {"X-API-KEY": API_KEY, "Content-Type": "application/json"}You can submit multiple texts at once, e.g. different paragraphs, and receive a corrected version of each one, along with markings of the changes made:
texts = [
"Ég fór í búðina og keypti mér eina mjólk.",
"Hún sagðist vera komin heim alla dagana."
]
resp = httpx.post(f"{BASE}/grammar", headers=HEADERS, json={"texts": texts})
resp.raise_for_status()
data = resp.json()
for i, result in enumerate(data["results"]):
print(f"Original text: {result['originalText']}")
print(f"Corrected text: {result['changedText']}")
if result["diffAnnotations"]:
for ann in result["diffAnnotations"]:
print(f"[{ann['changeType']}] \"{ann['origString']}\" -> \"{ann['changedString']}\"")
else:
print("No corrections")from pathlib import Path
import time
# 1. Get path and content type of media file
path = Path("YOUR_MEDIA_FILE_PATH")
ext_to_mime = {
".mp3": "audio/mpeg",
".wav": "audio/wav",
".m4a": "audio/mp4",
".ogg": "audio/ogg",
".flac": "audio/flac",
".mp4": "video/mp4",
".webm": "video/webm",
}
content_type = ext_to_mime.get(path.suffix.lower(), "audio/mpeg")
file_size = path.stat().st_size
# 2. Get presigned upload URL
presign = httpx.post(
f"{BASE}/uploads/presign",
headers=HEADERS,
json={"content_type": content_type, "file_size": file_size}
)
presign.raise_for_status()
presign_data = presign.json()
# 3: Upload file directly to storage
upload = httpx.put(
presign_data["uploadUrl"],
data=path.read_bytes(),
headers={
"Content-Type": content_type,
"Content-Length": str(file_size),
},
)
upload.raise_for_status()
# 4: Create transcription task
task = httpx.post(
f"{BASE}/asr/tasks",
headers=HEADERS,
json={"url": presign_data["objectUrl"], "input_language": "is"},
)
task.raise_for_status()
task_data = task.json()
task_id = task_data["taskId"]
# 5: Poll until completion
while True:
status_resp = httpx.get(f"{BASE}/asr/tasks/{task_id}", headers=HEADERS)
status_resp.raise_for_status()
status = status_resp.json()
if status["status"] == "completed":
for seg in status["result"]:
print(f" {seg['start']:6.1f}s - {seg['end']:6.1f}s : {seg['text']}")
break
elif status["status"] == "failed":
print(f"\nError: task failed: {status.get('error', 'unknown')}")
break
else:
print(f"Task status: {status['status']} (progress: {status['progress']*100:.1f}%)")
time.sleep(5) # Wait 5 seconds before polling againFor translations, three types of endpoints can be used to submit tasks:
/translate — for smaller text snippets where the client waits for a response (synchronous call, see example below).
/translate/text — for longer texts, works similarly to speech recognition where a task is initiated and the result is retrieved in a separate call with /translate/text/{id}.
/translate/segments — for translating timed segments (e.g., directly from speech recognition). It takes the same segments that the speech recognition returns and returns translated segments with timestamps. The result is retrieved with /translate/segments
Finally, /translate/tasks displays all translation tasks (both text and segments) and supports filtering by status and type. All endpoints support glossaries, and it is also possible to retrieve glossaries that belong to an organization on Málstaður.
Example of a simple text translation:
text = "The weather in Reykjavik is beautiful today, although a bit cold."
resp = httpx.post(f"{BASE}/translate", headers=HEADERS, json={"text": text, "targetLanguage": "is"})
resp.raise_for_status()
data = resp.json()
print(f"Translation: {data['text']}")Example of translating speech recognition output (e.g., for subtitles):
# (segments koma beint úr svari frá /asr/tasks/{id})
resp = httpx.post(
f"{BASE}/translate/segments",
headers=HEADERS,
json={"segments": asr_result["result"], "targetLanguage": "en"},
)
resp.raise_for_status()
task_id = resp.json()["taskId"]
while True:
status = httpx.get(f"{BASE}/translate/segments/{task_id}", headers=HEADERS).json()
if status["status"] == "completed":
for seg in status["result"]["segments"]:
print(f" {seg['start']:6.1f}s - {seg['end']:6.1f}s : {seg['text']}")
break
elif status["status"] == "failed":
print(f"Error: {status.get('error')}")
break
time.sleep(3)In the near future, we will be adding APIs for summarisation and plain language, among other things. We are also working on new functionality related to OCR and readability assessment, which will be launched in the coming months on both the web and in the APIs.
Please feel free to contact us via [email protected] or [email protected] if you have any questions regarding the APIs.
If you want to follow future projects at Miðeind, we can let you know when there is something new to report.