← Tilbake til dokumentasjon

API Referanse

REST API for bygningsenergisimulering med Bemify

Bemify Simulation API lar deg laste opp en energimodell (.sxi) og få tilbake energiresultater, energimerke (A–G) og TEK17-samsvarskontroll. API-et bruker en asynkron jobbkø — du sender inn en simulering og poller for resultater.

Base URL: https://api.bemify.no

Se kildekoden på GitHub

Hurtigstart

# Kjør en simulering med klimadata fra server
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "klimasted=Oslo"

# Poll for resultater
curl https://api.bemify.no/job/job_123456_1 \
  -H "Authorization: Bearer bmf_YOUR_TOKEN"

Windows: Bruk curl.exe --ssl-no-revoke. I PowerShell er curl et alias for Invoke-WebRequest med andre flagg, og Schannel gjør et revokasjonsoppslag som feiler mot Let's Encrypt-sertifikatet.

Autentisering

Alle simuleringsendepunkter krever en Bearer-token i Authorization-headeren:

Authorization: Bearer bmf_YOUR_TOKEN

Kontakt erlend@bemify.no for API-tilgang.

Endepunkter

POST /simulate

Start en ny simulering. Returnerer en jobb-ID for polling.

Content-Type: multipart/form-data

ParameterTypePåkrevdBeskrivelse
modelfilJaEnergimodell (.sxi)
climatefilBetingetEnergyPlus værfil (.epw). Gjensidig eksklusiv med klimasted. Ikke tillatt for tek17 og energimerke.
klimastedstrengBetingetKommunenavn (f.eks. Oslo). Alternativ til climate. Ikke tillatt for tek17.
simuleringstypestrengNeiaarssimulering (standard), energimerke, eller tek17
modellversjonstrengNeiVersjon av beregningsmodellen (f.eks. 2.1). Standard er nyeste versjon. Bruk GET /modellversjoner for gyldige verdier.

Regler for klimadata

  • Oppgi enten klimasted eller climate-fil, ikke begge
  • For tek17: Klimadata hentes alltid automatisk (TEK17-referanseklima). Oppgi ikke klimasted eller climate — forespørselen avvises med 400.
  • For energimerke: Kun klimasted godtas — EPW-filer avvises med 400. Energimerking krever en standard norsk klimasone for klimakorreksjonsfaktoren.
  • Bruk GET /klimasteder for å se gyldige kommunenavn.

Modellversjon

  • Utelatt eller tomt felt gir nyeste versjon
  • En ukjent verdi avvises med 400, og feilmeldingen lister gyldige verdier
  • Versjonen som faktisk ble brukt returneres som modelVersion — i både 202-responsen og GET /job/:jobId — også når parameteren utelates
  • Bruk GET /modellversjoner for å liste gyldige versjoner med utgivelsesdato og beskrivelse

Merk: En eldre modellversjon aktiverer den versjonens beregningsadferd, men filen parses av gjeldende SXI-importer, og resten av motoren er gjeldende bygg. En kjøring med eldre modellversjon er derfor ikke en bit-eksakt reproduksjon av hva en tidligere Bemify-versjon ville regnet ut for samme fil.

Respons (202):

{
  "jobId": "job_1712832645123_1",
  "position": 1,
  "modelVersion": "2.2",
  "message": "Simulering lagt i kø (posisjon 1). Poll /job/job_1712832645123_1 for status."
}

POST /validate

Valider en SXI-fil uten å kjøre en simulering. Returnerer en strukturert liste over feil, advarsler og manglende felt oppdaget under parsing og konvertering til Bemifys interne format. Nyttig for å iterere på modellfiler før en full simulering kjøres.

Content-Type: multipart/form-data

ParameterTypePåkrevdBeskrivelse
modelfilJaEnergimodell (.sxi)

Respons (200):

{
  "isValid": true,
  "errors": [],
  "warnings": [
    {
      "nodeId": "sone-1",
      "field": "setpoint",
      "message": "Standardverdi brukt",
      "defaultValue": 21
    }
  ],
  "missingData": [
    {
      "nodeId": "sone-1",
      "nodeType": "Sone",
      "missingFields": [
        { "field": "buildingSimulationProperties", "defaultValue": null }
      ]
    }
  ]
}

isValid: false betyr at filen kunne parses, men inneholder strukturelle feil — responsen er fortsatt 200. HTTP 400 returneres kun når filen ikke lar seg parse i det hele tatt (f.eks. ugyldig XML eller manglende .sxi-fil).

GET /job/:jobId

Sjekk jobbstatus og hent resultater. Krever autentisering. Brukere kan kun se egne jobber.

Statuser: queued | running | completed | error

Respons ved kø (200):

{
  "jobId": "job_1712832645123_1",
  "status": "queued",
  "modelVersion": "2.2",
  "queuedAt": "2026-04-11T10:30:45.123Z",
  "queueLength": 3
}

Respons ved fullført (200):

{
  "jobId": "job_1712832645123_1",
  "status": "completed",
  "modelVersion": "2.2",
  "queuedAt": "2026-04-11T10:30:45.123Z",
  "startedAt": "2026-04-11T10:30:50.456Z",
  "completedAt": "2026-04-11T10:32:15.789Z",
  "result": {
    "beregningspunkter": { "netto": { ... }, "brutto": { ... }, "tilfort": { ... }, "levert": { ... }, "nzebLevert": { ... } },
    "zones": [{ "id": "sone-1", "navn": "Sone 1", "area": 150.0 }],
    "energimerke": { ... },  // kun ved simuleringstype=energimerke
    "tek17": { ... }         // kun ved simuleringstype=tek17
  }
}

Respons ved feil (200):

{
  "jobId": "job_1712832645123_1",
  "status": "error",
  "completedAt": "2026-04-11T10:32:15.789Z",
  "error": "Feilmelding"
}

Merk: Resultater beholdes i opptil 30 minutter etter fullføring, men kan slettes tidligere dersom kapasitetsgrensen nås.

GET /klimasteder

Liste over alle tilgjengelige klimasteder. Ingen autentisering påkrevd.

{
  "locations": ["TEK17 Referanseklima", "Oslo", "Bergen", "Trondheim", ...],
  "count": 356
}

GET /modellversjoner

Liste over gyldige verdier for modellversjon. Ingen autentisering påkrevd.

{
  "defaultVersion": "2.2",
  "versions": [
    { "version": "1.0", "date": "2026-04-13", "description": "" },
    { "version": "1.1", "date": "2026-05-25", "description": "Forbedret beregning av varme- og luftstrøm gjennom skillekonstruksjoner" }
    // ... én per modellversjon
  ]
}

GET /health

Helsesjekk for serveren. Ingen autentisering påkrevd.

{
  "status": "ok",
  "timestamp": "2026-04-11T10:30:45.123Z",
  "queue": { "length": 0, "processing": false }
}

GET /queue

Køstatus. Ingen autentisering påkrevd.

{ "queueLength": 0, "isProcessing": false }

Simuleringstyper

VerdiBeskrivelseKlimadataEkstra resultatfelt
aarssimuleringHelårssimulering (standard)klimasted eller EPW-
energimerkeEnergimerkingKun klimastedenergimerke
tek17TEK17-samsvarskontrollAutomatisk (referanseklima)tek17

Resultatformat

Beregningspunkter (result.beregningspunkter)

Resultatene grupperes i beregningspunkter iht. NS 3031 (A–D). Punkt D har i tillegg en nZEB-variant:

PunktNøkkelBeskrivelse
AnettoNetto energibehov (bygningens behov)
BbruttoBrutto energibehov (inkl. systemtap)
CtilfortTilført energi (fra energikilder)
DlevertLevert energi (fra nett/energibærere)
D (nZEB)nzebLevertLevert energi, nZEB-variant (ekskluderer visse poster, ingen eksportkreditt)

Beregningspunktene deler ikke samme form — feltet med årstotaler heter ulikt per punkt. Punkt A (netto) og B (brutto) er nøklet på energipost i feltet energyResults (romoppvarming, ventilasjonsvarme, varmtvann, kjøling, belysning, utstyr m.m.). Punkt C (tilfort) bruker samme energipost-nøkling i feltet tilfortEnergi. Punkt D (levert) og D nZEB (nzebLevert) er i stedet nøklet på energibærer (elektrisitet, fjernvarme, fjernkjøling m.m.) i feltet levertEnergi.

Energimerke (result.energimerke)

Kun tilgjengelig når simuleringstype=energimerke og prosjektet har gyldig kommune og bygningskategori. En årssimulering kjører med prosjektets reelle inndata, og en TEK17-kjøring med NS 3031:2014-forutsetninger og referanseklima — ingen av dem gir det NS 3031:2025-grunnlaget et energimerke krever.

{
  "energimerke": "B",
  "totalArea": 250.0,
  "korreksjonsfaktor": 1.05,
  "klimakorrigertVektetSpesifikk": 92.3,
  "sumVektetSpesifikk": 96.9,
  "sumLevertEnergi": 24075,
  "sumSpesifikk": 96.3,
  "vektetKlimaavhengig": 42.1,
  "vektetIkkeKlimaavhengig": 54.8,
  "items": [
    {
      "kilde": "1 Levert elektrisitet",
      "levertEnergi_kWh": 21500,
      "spesifikk_kWhm2": 86.0,
      "vektingsfaktor": 1.0,
      "vektetSpesifikk_kWhm2": 86.0
    }
  ]
}
FeltEnhetBeskrivelse
energimerkeA–GEnergikarakter
klimakorrigertVektetSpesifikkkWh/(m²·år)Klimakorrigert vektet spesifikk levert energi (bestemmer karakteren)
sumVektetSpesifikkkWh/(m²·år)Vektet spesifikk levert energi (før klimakorreksjon)
sumLevertEnergikWh/årTotal levert energi
korreksjonsfaktorKlimakorreksjonsfaktor for kommune/bygningstype
itemsarrayFordeling per energibærer

TEK17-validering (result.tek17)

Kun tilgjengelig når simuleringstype=tek17.

{
  "erSamsvarsende": true,
  "energiramme": {
    "poster": [
      { "post": "1a", "beskrivelse": "Romoppvarming", "spesifikk_kWhm2": 12.3 }
    ],
    "totalBeregnet": 105.2,
    "forskriftskrav": 115.0,
    "status": "oppfylt",
    "bygningskategori": "Kontorbygning"
  },
  "minstekrav": {
    "rader": [
      { "bygningsdel": "U-verdi yttervegger, inkl. vegg mot uoppvarmet sone", "faktiskVerdi": 0.18, "kravVerdi": 0.22, "status": "oppfylt" }
    ],
    "samletStatus": "oppfylt"
  },
  "luftmengder": {
    "rader": [
      { "beskrivelse": "Spesifikk vifteeffekt (SFP)", "faktiskVerdi": 1.50, "kravVerdi": 2.00, "status": "oppfylt" }
    ],
    "samletStatus": "oppfylt"
  },
  "energiforsyning": {
    "brukerFossilBrensel": false,
    "fossilKilder": [],
    "punkt2Gjelder": true,
    "harSentralVarmesentral": true,
    "sentralAndelProsent": 85.2,
    "status": "oppfylt"
  },
  "oppsummering": {
    "antallOppfylt": 4,
    "antallIkkeOppfylt": 0,
    "antallIkkeRelevant": 0
  }
}

bygningsdel-etikettene i minstekrav.rader avhenger av modellversjon. Eksempelet over gjelder fra modellversjon 2.2.

Mulige status-verdier: "oppfylt", "ikke_oppfylt", "ikke_relevant".

Feilkoder

KodeBetydningEksempel
400Ugyldig forespørselManglende modellfil, ugyldig simuleringstype, ugyldig modellversjon, parameter oppgitt mer enn én gang, både klimasted og climate oppgitt
401Ikke autorisertManglende Authorization-header
403ForbudtUgyldig eller deaktivert API-nøkkel
404Ikke funnetJobb ikke funnet eller utløpt
413For storFil overstiger 10 MB-grensen
429For mange forespørslerGlobal eller per-nøkkel rate-grense nådd, eller for mange aktive jobber
502Bad gatewayKunne ikke hente klimadata fra oppstrømstjeneste
503Kø fullMaks 20 samtidige jobber

Begrensninger

GrenseVerdi
Global rate-grense30 forespørsler per minutt
Per-nøkkel POST /simulate6 forespørsler per minutt
Per-nøkkel POST /validate20 forespørsler per minutt
Per-nøkkel GET /job/:jobId120 forespørsler per minutt
Maks aktive jobber per nøkkel3
Maks filstørrelse10 MB per fil
Maks kødybde20 jobber
Simuleringstimeout10 minutter
Resultat-TTL30 minutter

Eksempler

curl

# Standard simulering med kommuneklima
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "klimasted=Oslo"

# Energimerkesimulering
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "klimasted=Oslo" \
  -F "simuleringstype=energimerke"

# TEK17-samsvarskontroll (klimadata hentes automatisk)
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "simuleringstype=tek17"

# Simulering med egen EPW-fil
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "climate=@oslo.epw"

# Simulering med fastlåst modellversjon
curl -X POST https://api.bemify.no/simulate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi" \
  -F "klimasted=Oslo" \
  -F "modellversjon=2.1"

# List gyldige modellversjoner
curl https://api.bemify.no/modellversjoner

# Valider en SXI-fil uten å kjøre simulering
curl -X POST https://api.bemify.no/validate \
  -H "Authorization: Bearer bmf_YOUR_TOKEN" \
  -F "model=@building.sxi"

# Poll for resultater
curl https://api.bemify.no/job/job_123456_1 \
  -H "Authorization: Bearer bmf_YOUR_TOKEN"

Python

import requests
import time

API_URL = "https://api.bemify.no"
TOKEN = "bmf_YOUR_TOKEN"
headers = {"Authorization": f"Bearer {TOKEN}"}

# Start simulering
with open("building.sxi", "rb") as model:
    resp = requests.post(
        f"{API_URL}/simulate",
        headers=headers,
        files={"model": model},
        data={
            "klimasted": "Oslo",
            "simuleringstype": "energimerke",
            # Valgfritt: utelat for å bruke nyeste modellversjon
            "modellversjon": "2.1",
        },
    )

job_id = resp.json()["jobId"]
print(f"Jobb startet: {job_id}")

# Poll for resultater
while True:
    status = requests.get(f"{API_URL}/job/{job_id}", headers=headers).json()
    if status["status"] in ("completed", "error"):
        break
    print(f"Status: {status['status']}...")
    time.sleep(2)

if status["status"] == "completed":
    result = status["result"]

    # Energimerke (kun for simuleringstype=energimerke)
    if "energimerke" in result:
        em = result["energimerke"]
        print(f"Energimerke: {em['energimerke']}")
        print(f"Vektet spesifikk: {em['klimakorrigertVektetSpesifikk']:.1f} kWh/(m²·år)")

    # TEK17 (kun for simuleringstype=tek17)
    if "tek17" in result:
        tek = result["tek17"]
        print(f"TEK17-samsvar: {tek['erSamsvarsende']}")
else:
    print(f"Feil: {status['error']}")